From 868787a5f97ae42e33f8d9084a7ad5f3c201a6ec Mon Sep 17 00:00:00 2001 From: Gary Scavone Date: Wed, 25 Sep 2013 11:21:51 +0200 Subject: [PATCH] Version 3.0 --- Bowed.cpp | 161 --- Bowed.h | 56 - Brass.cpp | 131 --- DLineA.cpp | 105 -- DLineL.cpp | 90 -- DLineN.cpp | 80 -- DLineN.h | 31 - FM4Op.cpp | 180 ---- FM4Op.h | 59 -- Flute.cpp | 175 ---- Flute.h | 64 -- FormSwep.cpp | 176 ---- FormSwep.h | 46 - HeavyMtl.cpp | 60 -- hierarch.txt => Hierarchy.txt | 110 +- Makefile.NeXT | 47 - Makefile.all | 68 -- Makefile.linux | 49 - Makefile.sgi | 49 - Marimba.cpp | 113 -- MatWvOut.cpp | 214 ---- MatWvOut.h | 46 - Moog1.cpp | 107 -- Object.h | 134 --- OnePole.cpp | 81 -- PercFlut.cpp | 54 - README-Linux.txt | 41 +- README-SGI.txt | 49 +- README-Win.txt | 47 +- README.txt | 94 +- RTSoundIO.cpp | 492 --------- RTSoundIO.h | 61 -- RTWvOut.cpp | 56 - RTWvOut.h | 46 - RawInterp.cpp | 245 ----- RawInterp.h | 52 - RawLoop.cpp | 160 --- RawLoop.h | 40 - RawShot.cpp | 178 ---- RawShot.h | 49 - RawWave.cpp | 42 - RawWave.h | 25 - ReleaseNotes.txt | 31 +- Rhodey.cpp | 57 -- SKINI11.cpp | 345 ------- SKINI09.txt => SKINI11.txt | 282 +++-- ADSR.cpp => STK/ADSR.cpp | 0 ADSR.h => STK/ADSR.h | 0 AgogoBel.cpp => STK/AgogoBel.cpp | 29 +- AgogoBel.h => STK/AgogoBel.h | 2 +- BeeThree.cpp => STK/BeeThree.cpp | 44 +- BeeThree.h => STK/BeeThree.h | 0 BiQuad.cpp => STK/BiQuad.cpp | 2 +- BiQuad.h => STK/BiQuad.h | 0 BowTabl.cpp => STK/BowTabl.cpp | 0 BowTabl.h => STK/BowTabl.h | 4 + STK/Bowed.cpp | 166 +++ STK/Bowed.h | 56 + STK/BowedBar.cpp | 318 ++++++ STK/BowedBar.h | 62 ++ STK/Brass.cpp | 135 +++ Brass.h => STK/Brass.h | 8 +- Clarinet.cpp => STK/Clarinet.cpp | 6 +- Clarinet.h => STK/Clarinet.h | 4 +- DCBlock.cpp => STK/DCBlock.cpp | 25 +- DCBlock.h => STK/DCBlock.h | 15 +- STK/DLineA.cpp | 107 ++ DLineA.h => STK/DLineA.h | 12 +- STK/DLineL.cpp | 77 ++ DLineL.h => STK/DLineL.h | 25 +- STK/DLineN.cpp | 70 ++ STK/DLineN.h | 35 + {Debug => STK/Debug}/.placeholder | 0 DrumSynt.cpp => STK/DrumSynt.cpp | 43 +- DrumSynt.h => STK/DrumSynt.h | 27 +- Envelope.cpp => STK/Envelope.cpp | 87 +- Envelope.h => STK/Envelope.h | 34 +- FM4Alg3.cpp => STK/FM4Alg3.cpp | 0 FM4Alg3.h => STK/FM4Alg3.h | 0 FM4Alg4.cpp => STK/FM4Alg4.cpp | 0 FM4Alg4.h => STK/FM4Alg4.h | 0 FM4Alg5.cpp => STK/FM4Alg5.cpp | 34 +- FM4Alg5.h => STK/FM4Alg5.h | 0 FM4Alg6.cpp => STK/FM4Alg6.cpp | 0 FM4Alg6.h => STK/FM4Alg6.h | 0 FM4Alg8.cpp => STK/FM4Alg8.cpp | 0 FM4Alg8.h => STK/FM4Alg8.h | 0 STK/FM4Op.cpp | 184 ++++ STK/FM4Op.h | 59 ++ FMVoices.cpp => STK/FMVoices.cpp | 33 +- FMVoices.h => STK/FMVoices.h | 0 Filter.cpp => STK/Filter.cpp | 0 Filter.h => STK/Filter.h | 0 STK/Flute.cpp | 178 ++++ STK/Flute.h | 64 ++ STK/FormSwep.cpp | 139 +++ STK/FormSwep.h | 46 + STK/HeavyMtl.cpp | 68 ++ HeavyMtl.h => STK/HeavyMtl.h | 0 Instrmnt.cpp => STK/Instrmnt.cpp | 0 Instrmnt.h => STK/Instrmnt.h | 0 JCRev.cpp => STK/JCRev.cpp | 0 JCRev.h => STK/JCRev.h | 0 JetTabl.cpp => STK/JetTabl.cpp | 0 JetTabl.h => STK/JetTabl.h | 4 + LipFilt.cpp => STK/LipFilt.cpp | 0 LipFilt.h => STK/LipFilt.h | 0 MD2SKINI.cpp => STK/MD2SKINI.cpp | 314 +++--- MIDIIO.cpp => STK/MIDIIO.cpp | 171 ++-- MIDIIO.h => STK/MIDIIO.h | 2 +- Mandolin.cpp => STK/Mandolin.cpp | 85 +- Mandolin.h => STK/Mandolin.h | 10 +- STK/Marimba.cpp | 116 +++ Marimba.h => STK/Marimba.h | 0 STK/MatWvIn.cpp | 178 ++++ STK/MatWvIn.h | 33 + STK/MatWvOut.cpp | 199 ++++ STK/MatWvOut.h | 44 + Modal4.cpp => STK/Modal4.cpp | 63 +- Modal4.h => STK/Modal4.h | 7 +- Modulatr.cpp => STK/Modulatr.cpp | 45 +- Modulatr.h => STK/Modulatr.h | 34 +- STK/Moog1.cpp | 114 +++ Moog1.h => STK/Moog1.h | 30 +- NRev.cpp => STK/NRev.cpp | 0 NRev.h => STK/NRev.h | 0 Noise.cpp => STK/Noise.cpp | 7 +- Noise.h => STK/Noise.h | 0 Object.cpp => STK/Object.cpp | 0 STK/Object.h | 131 +++ STK/OnePole.cpp | 62 ++ OnePole.h => STK/OnePole.h | 0 OneZero.cpp => STK/OneZero.cpp | 46 +- OneZero.h => STK/OneZero.h | 0 PRCRev.cpp => STK/PRCRev.cpp | 0 PRCRev.h => STK/PRCRev.h | 0 STK/PercFlut.cpp | 63 ++ PercFlut.h => STK/PercFlut.h | 0 Plucked.cpp => STK/Plucked.cpp | 0 Plucked.h => STK/Plucked.h | 0 Plucked2.cpp => STK/Plucked2.cpp | 0 Plucked2.h => STK/Plucked2.h | 0 STK/PoleZero.cpp | 63 ++ STK/PoleZero.h | 30 + STK/RTDuplex.cpp | 97 ++ STK/RTDuplex.h | 38 + STK/RTSoundIO.cpp | 963 ++++++++++++++++++ STK/RTSoundIO.h | 71 ++ STK/RTWvIn.cpp | 126 +++ STK/RTWvIn.h | 44 + STK/RTWvOut.cpp | 58 ++ STK/RTWvOut.h | 32 + STK/RawWvIn.cpp | 79 ++ STK/RawWvIn.h | 26 + STK/RawWvOut.cpp | 106 ++ STK/RawWvOut.h | 38 + ReedTabl.cpp => STK/ReedTabl.cpp | 0 ReedTabl.h => STK/ReedTabl.h | 5 + {Release => STK/Release}/.placeholder | 0 Reverb.cpp => STK/Reverb.cpp | 0 Reverb.h => STK/Reverb.h | 0 STK/Rhodey.cpp | 66 ++ Rhodey.h => STK/Rhodey.h | 0 STK/SKINI11.cpp | 344 +++++++ SKINI11.h => STK/SKINI11.h | 2 +- SKINI11.msg => STK/SKINI11.msg | 0 SKINI11.tbl => STK/SKINI11.tbl | 0 SamplFlt.cpp => STK/SamplFlt.cpp | 0 SamplFlt.h => STK/SamplFlt.h | 16 +- Sampler.cpp => STK/Sampler.cpp | 40 +- STK/Sampler.h | 42 + STK/Shakers.cpp | 737 ++++++++++++++ STK/Shakers.h | 73 ++ STK/Simple.cpp | 115 +++ STK/Simple.h | 44 + SingWave.cpp => STK/SingWave.cpp | 8 +- SingWave.h => STK/SingWave.h | 0 STK/SndWvIn.cpp | 102 ++ STK/SndWvIn.h | 25 + STK/SndWvOut.cpp | 133 +++ STK/SndWvOut.h | 36 + SubNoise.cpp => STK/SubNoise.cpp | 0 SubNoise.h => STK/SubNoise.h | 0 STK/TablLook.cpp | 96 ++ STK/TablLook.h | 31 + STK/TubeBell.cpp | 62 ++ TubeBell.h => STK/TubeBell.h | 0 STK/TwoPole.cpp | 60 ++ TwoPole.h => STK/TwoPole.h | 0 STK/TwoZero.cpp | 53 + TwoZero.h => STK/TwoZero.h | 0 Vibraphn.cpp => STK/Vibraphn.cpp | 35 +- Vibraphn.h => STK/Vibraphn.h | 0 VoicForm.cpp => STK/VoicForm.cpp | 71 +- VoicForm.h => STK/VoicForm.h | 12 +- VoicMang.cpp => STK/VoicMang.cpp | 0 VoicMang.h => STK/VoicMang.h | 0 STK/WavWvIn.cpp | 103 ++ STK/WavWvIn.h | 26 + STK/WavWvOut.cpp | 158 +++ WavWvOut.h => STK/WavWvOut.h | 19 +- STK/Wurley.cpp | 62 ++ Wurley.h => STK/Wurley.h | 0 STK/WvIn.cpp | 222 ++++ STK/WvIn.h | 51 + STK/WvOut.cpp | 27 + STK/WvOut.h | 27 + mandplyr.cpp => STK/mandplyr.cpp | 0 mandplyr.h => STK/mandplyr.h | 0 phontabl.h => STK/phontabl.h | 0 swapstuf.cpp => STK/swapstuf.cpp | 23 +- STK/swapstuf.h | 6 + STK98v2.ncb | Bin 1033216 -> 0 bytes Sampler.h | 43 - Shakers.cpp | 643 ------------ Shakers.h | 35 - Simple.cpp | 111 -- Simple.h | 44 - SndWvOut.cpp | 168 --- SndWvOut.h | 44 - TCLSpecs/.swp | Bin 24576 -> 0 bytes TCLSpecs/GUIDrums | 2 - TCLSpecs/GUIPhysical | 2 - TCLSpecs/GUIPlukStruk | 2 - TCLSpecs/GUIShakers | 2 - TCLSpecs/GUIVoice | 2 - TubeBell.cpp | 53 - TwoPole.cpp | 61 -- TwoZero.cpp | 53 - WavWvOut.cpp | 189 ---- Wurley.cpp | 53 - WvOut.cpp | 21 - WvOut.h | 23 - effects/Chorus.cpp | 125 +++ effects/Chorus.h | 37 + effects/Debug/.placeholder | 0 effects/Echo.cpp | 44 + effects/Echo.h | 30 + effects/GUIeffects | 1 + effects/Makefile | 60 ++ effects/PitShift.cpp | 106 ++ effects/PitShift.h | 32 + effects/README-effects.txt | 16 + effects/Release/.placeholder | 0 effects/effects.cpp | 159 +++ effects/effects.dsp | 243 +++++ effects/effects.dsw | 29 + effects/effects.opt | Bin 0 -> 48640 bytes effects/effects.plg | 80 ++ effects/tcl/Effects.tcl | 219 ++++ effects/threads.cpp | 282 +++++ effects/threads.h | 27 + include/dsound.h | 863 ---------------- mus151/GUITwoOsc | 2 +- mus151/MUS151.cpp | 266 +---- mus151/MUS151.dsp | 60 +- mus151/MUS151.ncb | Bin 58368 -> 107520 bytes STK98v2.opt => mus151/MUS151.opt | Bin 53760 -> 48640 bytes mus151/Makefile | 25 +- mus151/README-mus151.txt | 14 +- mus151/TwoOsc.cpp | 6 +- mus151/TwoOsc.h | 8 +- miditabl.h => mus151/miditabl.h | 2 +- mus151/tcl/TwoWaves.tcl | 36 +- mus151/threads.cpp | 282 +++++ mus151/threads.h | 27 + rawwaves/silence.aiff | Bin 518 -> 0 bytes scores/tstloops.sco | 60 -- swapstuf.h | 3 - syntmono.cpp | 603 ----------- syntmono.plg | 242 ----- syntmono/GUIBowedBar | 1 + syntmono/GUIDrums | 1 + syntmono/GUIPhysical | 1 + syntmono/GUIPlukStruk | 1 + syntmono/GUIShakers | 1 + syntmono/GUIVoice | 1 + {TCLSpecs => syntmono}/MIDIPhysical | 1 - {TCLSpecs => syntmono}/MIDIPlukStruk | 1 - {TCLSpecs => syntmono}/MIDIVoice | 1 - Makefile => syntmono/Makefile | 43 +- syntmono/Makefile.sgi | 74 ++ syntmono/Release/.placeholder | 0 STK98v2.dsw => syntmono/STK.dsw | 0 syntmono/STK.opt | Bin 0 -> 53760 bytes syntmono/STKdemo | 1 + md2skini.dsp => syntmono/md2skini.dsp | 22 +- md2skini.plg => syntmono/md2skini.plg | 18 +- rawwaves/mditabl.h => syntmono/miditabl.h | 36 +- {scores => syntmono/scores}/bookert.ski | 0 {scores => syntmono/scores}/capture.ski | 0 {scores => syntmono/scores}/chords.ski | 0 {scores => syntmono/scores}/doogie.ski | 0 {scores => syntmono/scores}/drumfunk.ski | 0 {scores => syntmono/scores}/drumtest.ski | 0 {scores => syntmono/scores}/duelingb.ski | 0 {scores => syntmono/scores}/fiddle.ski | 0 {scores => syntmono/scores}/flutbach.ski | 0 {scores => syntmono/scores}/funicula.ski | 0 {scores => syntmono/scores}/funskini.ski | 0 {scores => syntmono/scores}/instructions | 13 +- {scores => syntmono/scores}/lacrymos.ski | 0 {scores => syntmono/scores}/mandtune.ski | 0 {scores => syntmono/scores}/marimba2.ski | 0 {scores => syntmono/scores}/marimtst.ski | 0 {scores => syntmono/scores}/misacrio.ski | 0 {scores => syntmono/scores}/morazbel.ski | 0 {scores => syntmono/scores}/organs.ski | 0 {scores => syntmono/scores}/pickdamp.ski | 0 {scores => syntmono/scores}/pictures.ski | 0 {scores => syntmono/scores}/riderson.ski | 0 {scores => syntmono/scores}/scales.ski | 0 {scores => syntmono/scores}/shaktest.ski | 0 {scores => syntmono/scores}/simplgft.ski | 0 {scores => syntmono/scores}/spain.ski | 0 {scores => syntmono/scores}/spain0.ski | 0 {scores => syntmono/scores}/streetsf.ski | 0 syntmono/scores/test.ski | 3 + {scores => syntmono/scores}/thecars.ski | 0 {scores => syntmono/scores}/tubebell.ski | 0 {scores => syntmono/scores}/vocaliz.ski | 0 syntmono/syntmono.cpp | 170 ++++ syntmono.dsp => syntmono/syntmono.dsp | 348 ++++--- syntmono/syntmono.plg | 107 ++ syntmono/tcl/TCLBowedBar.tcl | 233 +++++ syntmono/tcl/TCLDemo.tcl | 530 ++++++++++ {TCLSpecs => syntmono/tcl}/TCLDrums.tcl | 36 +- {TCLSpecs => syntmono/tcl}/TCLPhys.tcl | 46 +- {TCLSpecs => syntmono/tcl}/TCLShakers.tcl | 110 +- {TCLSpecs => syntmono/tcl}/TCLStruk.tcl | 38 +- {TCLSpecs => syntmono/tcl}/TCLVoice.tcl | 35 +- {TCLSpecs => syntmono/tcl}/bitmaps/KFMod.xbm | 0 {TCLSpecs => syntmono/tcl}/bitmaps/KFiddl.xbm | 0 {TCLSpecs => syntmono/tcl}/bitmaps/KFloot.xbm | 0 {TCLSpecs => syntmono/tcl}/bitmaps/KHose.xbm | 0 {TCLSpecs => syntmono/tcl}/bitmaps/KModal.xbm | 0 {TCLSpecs => syntmono/tcl}/bitmaps/KPluk.xbm | 0 .../tcl}/bitmaps/KVoicForm.xbm | 0 .../tcl}/bitmaps/KVoiceFM.xbm | 0 {TCLSpecs => syntmono/tcl}/bitmaps/Klar.xbm | 0 {TCLSpecs => syntmono/tcl}/bitmaps/kasm.xbm | 0 syntmono/tcl/bitmaps/phism.xbm | 90 ++ {TCLSpecs => syntmono/tcl}/bitmaps/prc.xbm | 0 .../tcl}/bitmaps/prcFunny.xbm | 0 syntmono/threads.cpp | 282 +++++ syntmono/threads.h | 27 + syntmono/utilities.cpp | 243 +++++ syntmono/utilities.h | 15 + 348 files changed, 12471 insertions(+), 9135 deletions(-) delete mode 100644 Bowed.cpp delete mode 100644 Bowed.h delete mode 100644 Brass.cpp delete mode 100644 DLineA.cpp delete mode 100644 DLineL.cpp delete mode 100644 DLineN.cpp delete mode 100644 DLineN.h delete mode 100644 FM4Op.cpp delete mode 100644 FM4Op.h delete mode 100644 Flute.cpp delete mode 100644 Flute.h delete mode 100644 FormSwep.cpp delete mode 100644 FormSwep.h delete mode 100644 HeavyMtl.cpp rename hierarch.txt => Hierarchy.txt (60%) delete mode 100644 Makefile.NeXT delete mode 100644 Makefile.all delete mode 100644 Makefile.linux delete mode 100644 Makefile.sgi delete mode 100644 Marimba.cpp delete mode 100644 MatWvOut.cpp delete mode 100644 MatWvOut.h delete mode 100644 Moog1.cpp delete mode 100644 Object.h delete mode 100644 OnePole.cpp delete mode 100644 PercFlut.cpp delete mode 100644 RTSoundIO.cpp delete mode 100644 RTSoundIO.h delete mode 100644 RTWvOut.cpp delete mode 100644 RTWvOut.h delete mode 100644 RawInterp.cpp delete mode 100644 RawInterp.h delete mode 100644 RawLoop.cpp delete mode 100644 RawLoop.h delete mode 100644 RawShot.cpp delete mode 100644 RawShot.h delete mode 100644 RawWave.cpp delete mode 100644 RawWave.h delete mode 100644 Rhodey.cpp delete mode 100644 SKINI11.cpp rename SKINI09.txt => SKINI11.txt (77%) rename ADSR.cpp => STK/ADSR.cpp (100%) rename ADSR.h => STK/ADSR.h (100%) rename AgogoBel.cpp => STK/AgogoBel.cpp (77%) rename AgogoBel.h => STK/AgogoBel.h (93%) rename BeeThree.cpp => STK/BeeThree.cpp (55%) rename BeeThree.h => STK/BeeThree.h (100%) rename BiQuad.cpp => STK/BiQuad.cpp (97%) rename BiQuad.h => STK/BiQuad.h (100%) rename BowTabl.cpp => STK/BowTabl.cpp (100%) rename BowTabl.h => STK/BowTabl.h (90%) create mode 100644 STK/Bowed.cpp create mode 100644 STK/Bowed.h create mode 100644 STK/BowedBar.cpp create mode 100644 STK/BowedBar.h create mode 100644 STK/Brass.cpp rename Brass.h => STK/Brass.h (91%) rename Clarinet.cpp => STK/Clarinet.cpp (95%) rename Clarinet.h => STK/Clarinet.h (97%) rename DCBlock.cpp => STK/DCBlock.cpp (58%) rename DCBlock.h => STK/DCBlock.h (66%) create mode 100644 STK/DLineA.cpp rename DLineA.h => STK/DLineA.h (79%) create mode 100644 STK/DLineL.cpp rename DLineL.h => STK/DLineL.h (68%) create mode 100644 STK/DLineN.cpp create mode 100644 STK/DLineN.h rename {Debug => STK/Debug}/.placeholder (100%) rename DrumSynt.cpp => STK/DrumSynt.cpp (83%) rename DrumSynt.h => STK/DrumSynt.h (58%) rename Envelope.cpp => STK/Envelope.cpp (56%) rename Envelope.h => STK/Envelope.h (65%) rename FM4Alg3.cpp => STK/FM4Alg3.cpp (100%) rename FM4Alg3.h => STK/FM4Alg3.h (100%) rename FM4Alg4.cpp => STK/FM4Alg4.cpp (100%) rename FM4Alg4.h => STK/FM4Alg4.h (100%) rename FM4Alg5.cpp => STK/FM4Alg5.cpp (52%) rename FM4Alg5.h => STK/FM4Alg5.h (100%) rename FM4Alg6.cpp => STK/FM4Alg6.cpp (100%) rename FM4Alg6.h => STK/FM4Alg6.h (100%) rename FM4Alg8.cpp => STK/FM4Alg8.cpp (100%) rename FM4Alg8.h => STK/FM4Alg8.h (100%) create mode 100644 STK/FM4Op.cpp create mode 100644 STK/FM4Op.h rename FMVoices.cpp => STK/FMVoices.cpp (83%) rename FMVoices.h => STK/FMVoices.h (100%) rename Filter.cpp => STK/Filter.cpp (100%) rename Filter.h => STK/Filter.h (100%) create mode 100644 STK/Flute.cpp create mode 100644 STK/Flute.h create mode 100644 STK/FormSwep.cpp create mode 100644 STK/FormSwep.h create mode 100644 STK/HeavyMtl.cpp rename HeavyMtl.h => STK/HeavyMtl.h (100%) rename Instrmnt.cpp => STK/Instrmnt.cpp (100%) rename Instrmnt.h => STK/Instrmnt.h (100%) rename JCRev.cpp => STK/JCRev.cpp (100%) rename JCRev.h => STK/JCRev.h (100%) rename JetTabl.cpp => STK/JetTabl.cpp (100%) rename JetTabl.h => STK/JetTabl.h (91%) rename LipFilt.cpp => STK/LipFilt.cpp (100%) rename LipFilt.h => STK/LipFilt.h (100%) rename MD2SKINI.cpp => STK/MD2SKINI.cpp (59%) rename MIDIIO.cpp => STK/MIDIIO.cpp (77%) rename MIDIIO.h => STK/MIDIIO.h (95%) rename Mandolin.cpp => STK/Mandolin.cpp (66%) rename Mandolin.h => STK/Mandolin.h (86%) create mode 100644 STK/Marimba.cpp rename Marimba.h => STK/Marimba.h (100%) create mode 100644 STK/MatWvIn.cpp create mode 100644 STK/MatWvIn.h create mode 100644 STK/MatWvOut.cpp create mode 100644 STK/MatWvOut.h rename Modal4.cpp => STK/Modal4.cpp (73%) rename Modal4.h => STK/Modal4.h (94%) rename Modulatr.cpp => STK/Modulatr.cpp (53%) rename Modulatr.h => STK/Modulatr.h (52%) create mode 100644 STK/Moog1.cpp rename Moog1.h => STK/Moog1.h (51%) rename NRev.cpp => STK/NRev.cpp (100%) rename NRev.h => STK/NRev.h (100%) rename Noise.cpp => STK/Noise.cpp (87%) rename Noise.h => STK/Noise.h (100%) rename Object.cpp => STK/Object.cpp (100%) create mode 100644 STK/Object.h create mode 100644 STK/OnePole.cpp rename OnePole.h => STK/OnePole.h (100%) rename OneZero.cpp => STK/OneZero.cpp (50%) rename OneZero.h => STK/OneZero.h (100%) rename PRCRev.cpp => STK/PRCRev.cpp (100%) rename PRCRev.h => STK/PRCRev.h (100%) create mode 100644 STK/PercFlut.cpp rename PercFlut.h => STK/PercFlut.h (100%) rename Plucked.cpp => STK/Plucked.cpp (100%) rename Plucked.h => STK/Plucked.h (100%) rename Plucked2.cpp => STK/Plucked2.cpp (100%) rename Plucked2.h => STK/Plucked2.h (100%) create mode 100644 STK/PoleZero.cpp create mode 100644 STK/PoleZero.h create mode 100644 STK/RTDuplex.cpp create mode 100644 STK/RTDuplex.h create mode 100644 STK/RTSoundIO.cpp create mode 100644 STK/RTSoundIO.h create mode 100644 STK/RTWvIn.cpp create mode 100644 STK/RTWvIn.h create mode 100644 STK/RTWvOut.cpp create mode 100644 STK/RTWvOut.h create mode 100644 STK/RawWvIn.cpp create mode 100644 STK/RawWvIn.h create mode 100644 STK/RawWvOut.cpp create mode 100644 STK/RawWvOut.h rename ReedTabl.cpp => STK/ReedTabl.cpp (100%) rename ReedTabl.h => STK/ReedTabl.h (92%) rename {Release => STK/Release}/.placeholder (100%) rename Reverb.cpp => STK/Reverb.cpp (100%) rename Reverb.h => STK/Reverb.h (100%) create mode 100644 STK/Rhodey.cpp rename Rhodey.h => STK/Rhodey.h (100%) create mode 100644 STK/SKINI11.cpp rename SKINI11.h => STK/SKINI11.h (97%) rename SKINI11.msg => STK/SKINI11.msg (100%) rename SKINI11.tbl => STK/SKINI11.tbl (100%) rename SamplFlt.cpp => STK/SamplFlt.cpp (100%) rename SamplFlt.h => STK/SamplFlt.h (71%) rename Sampler.cpp => STK/Sampler.cpp (50%) create mode 100644 STK/Sampler.h create mode 100644 STK/Shakers.cpp create mode 100644 STK/Shakers.h create mode 100644 STK/Simple.cpp create mode 100644 STK/Simple.h rename SingWave.cpp => STK/SingWave.cpp (97%) rename SingWave.h => STK/SingWave.h (100%) create mode 100644 STK/SndWvIn.cpp create mode 100644 STK/SndWvIn.h create mode 100644 STK/SndWvOut.cpp create mode 100644 STK/SndWvOut.h rename SubNoise.cpp => STK/SubNoise.cpp (100%) rename SubNoise.h => STK/SubNoise.h (100%) create mode 100644 STK/TablLook.cpp create mode 100644 STK/TablLook.h create mode 100644 STK/TubeBell.cpp rename TubeBell.h => STK/TubeBell.h (100%) create mode 100644 STK/TwoPole.cpp rename TwoPole.h => STK/TwoPole.h (100%) create mode 100644 STK/TwoZero.cpp rename TwoZero.h => STK/TwoZero.h (100%) rename Vibraphn.cpp => STK/Vibraphn.cpp (68%) rename Vibraphn.h => STK/Vibraphn.h (100%) rename VoicForm.cpp => STK/VoicForm.cpp (78%) rename VoicForm.h => STK/VoicForm.h (85%) rename VoicMang.cpp => STK/VoicMang.cpp (100%) rename VoicMang.h => STK/VoicMang.h (100%) create mode 100644 STK/WavWvIn.cpp create mode 100644 STK/WavWvIn.h create mode 100644 STK/WavWvOut.cpp rename WavWvOut.h => STK/WavWvOut.h (68%) create mode 100644 STK/Wurley.cpp rename Wurley.h => STK/Wurley.h (100%) create mode 100644 STK/WvIn.cpp create mode 100644 STK/WvIn.h create mode 100644 STK/WvOut.cpp create mode 100644 STK/WvOut.h rename mandplyr.cpp => STK/mandplyr.cpp (100%) rename mandplyr.h => STK/mandplyr.h (100%) rename phontabl.h => STK/phontabl.h (100%) rename swapstuf.cpp => STK/swapstuf.cpp (54%) create mode 100644 STK/swapstuf.h delete mode 100644 STK98v2.ncb delete mode 100644 Sampler.h delete mode 100644 Shakers.cpp delete mode 100644 Shakers.h delete mode 100644 Simple.cpp delete mode 100644 Simple.h delete mode 100644 SndWvOut.cpp delete mode 100644 SndWvOut.h delete mode 100644 TCLSpecs/.swp delete mode 100755 TCLSpecs/GUIDrums delete mode 100755 TCLSpecs/GUIPhysical delete mode 100755 TCLSpecs/GUIPlukStruk delete mode 100755 TCLSpecs/GUIShakers delete mode 100755 TCLSpecs/GUIVoice delete mode 100644 TubeBell.cpp delete mode 100644 TwoPole.cpp delete mode 100644 TwoZero.cpp delete mode 100644 WavWvOut.cpp delete mode 100644 Wurley.cpp delete mode 100644 WvOut.cpp delete mode 100644 WvOut.h create mode 100644 effects/Chorus.cpp create mode 100644 effects/Chorus.h create mode 100644 effects/Debug/.placeholder create mode 100644 effects/Echo.cpp create mode 100644 effects/Echo.h create mode 100755 effects/GUIeffects create mode 100644 effects/Makefile create mode 100644 effects/PitShift.cpp create mode 100644 effects/PitShift.h create mode 100644 effects/README-effects.txt create mode 100644 effects/Release/.placeholder create mode 100644 effects/effects.cpp create mode 100644 effects/effects.dsp create mode 100644 effects/effects.dsw create mode 100644 effects/effects.opt create mode 100644 effects/effects.plg create mode 100644 effects/tcl/Effects.tcl create mode 100644 effects/threads.cpp create mode 100644 effects/threads.h delete mode 100644 include/dsound.h rename STK98v2.opt => mus151/MUS151.opt (61%) rename miditabl.h => mus151/miditabl.h (97%) create mode 100644 mus151/threads.cpp create mode 100644 mus151/threads.h delete mode 100644 rawwaves/silence.aiff delete mode 100644 scores/tstloops.sco delete mode 100644 swapstuf.h delete mode 100644 syntmono.cpp delete mode 100644 syntmono.plg create mode 100755 syntmono/GUIBowedBar create mode 100755 syntmono/GUIDrums create mode 100755 syntmono/GUIPhysical create mode 100755 syntmono/GUIPlukStruk create mode 100755 syntmono/GUIShakers create mode 100755 syntmono/GUIVoice rename {TCLSpecs => syntmono}/MIDIPhysical (85%) rename {TCLSpecs => syntmono}/MIDIPlukStruk (85%) rename {TCLSpecs => syntmono}/MIDIVoice (85%) rename Makefile => syntmono/Makefile (57%) create mode 100644 syntmono/Makefile.sgi create mode 100644 syntmono/Release/.placeholder rename STK98v2.dsw => syntmono/STK.dsw (100%) create mode 100755 syntmono/STK.opt create mode 100755 syntmono/STKdemo rename md2skini.dsp => syntmono/md2skini.dsp (86%) rename md2skini.plg => syntmono/md2skini.plg (60%) mode change 100644 => 100755 rename rawwaves/mditabl.h => syntmono/miditabl.h (96%) rename {scores => syntmono/scores}/bookert.ski (100%) rename {scores => syntmono/scores}/capture.ski (100%) rename {scores => syntmono/scores}/chords.ski (100%) rename {scores => syntmono/scores}/doogie.ski (100%) rename {scores => syntmono/scores}/drumfunk.ski (100%) rename {scores => syntmono/scores}/drumtest.ski (100%) rename {scores => syntmono/scores}/duelingb.ski (100%) rename {scores => syntmono/scores}/fiddle.ski (100%) rename {scores => syntmono/scores}/flutbach.ski (100%) rename {scores => syntmono/scores}/funicula.ski (100%) rename {scores => syntmono/scores}/funskini.ski (100%) rename {scores => syntmono/scores}/instructions (57%) rename {scores => syntmono/scores}/lacrymos.ski (100%) rename {scores => syntmono/scores}/mandtune.ski (100%) rename {scores => syntmono/scores}/marimba2.ski (100%) rename {scores => syntmono/scores}/marimtst.ski (100%) rename {scores => syntmono/scores}/misacrio.ski (100%) rename {scores => syntmono/scores}/morazbel.ski (100%) rename {scores => syntmono/scores}/organs.ski (100%) rename {scores => syntmono/scores}/pickdamp.ski (100%) rename {scores => syntmono/scores}/pictures.ski (100%) rename {scores => syntmono/scores}/riderson.ski (100%) rename {scores => syntmono/scores}/scales.ski (100%) rename {scores => syntmono/scores}/shaktest.ski (100%) rename {scores => syntmono/scores}/simplgft.ski (100%) rename {scores => syntmono/scores}/spain.ski (100%) rename {scores => syntmono/scores}/spain0.ski (100%) rename {scores => syntmono/scores}/streetsf.ski (100%) create mode 100644 syntmono/scores/test.ski rename {scores => syntmono/scores}/thecars.ski (100%) rename {scores => syntmono/scores}/tubebell.ski (100%) rename {scores => syntmono/scores}/vocaliz.ski (100%) create mode 100644 syntmono/syntmono.cpp rename syntmono.dsp => syntmono/syntmono.dsp (62%) create mode 100644 syntmono/syntmono.plg create mode 100644 syntmono/tcl/TCLBowedBar.tcl create mode 100644 syntmono/tcl/TCLDemo.tcl rename {TCLSpecs => syntmono/tcl}/TCLDrums.tcl (79%) rename {TCLSpecs => syntmono/tcl}/TCLPhys.tcl (84%) rename {TCLSpecs => syntmono/tcl}/TCLShakers.tcl (61%) rename {TCLSpecs => syntmono/tcl}/TCLStruk.tcl (86%) rename {TCLSpecs => syntmono/tcl}/TCLVoice.tcl (84%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KFMod.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KFiddl.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KFloot.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KHose.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KModal.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KPluk.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KVoicForm.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/KVoiceFM.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/Klar.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/kasm.xbm (100%) create mode 100644 syntmono/tcl/bitmaps/phism.xbm rename {TCLSpecs => syntmono/tcl}/bitmaps/prc.xbm (100%) rename {TCLSpecs => syntmono/tcl}/bitmaps/prcFunny.xbm (100%) create mode 100644 syntmono/threads.cpp create mode 100644 syntmono/threads.h create mode 100644 syntmono/utilities.cpp create mode 100644 syntmono/utilities.h diff --git a/Bowed.cpp b/Bowed.cpp deleted file mode 100644 index 3bee94a..0000000 --- a/Bowed.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************/ -/* Bowed String model ala Smith */ -/* after McIntyre, Schumacher, Woodhouse */ -/* by Perry Cook, 1995-96 */ -/* */ -/* This is a waveguide model, and thus */ -/* relates to various Stanford Univ. */ -/* and possibly Yamaha and other patents.*/ -/* */ -/* Controls: CONTROL1 = bowPressure */ -/* CONTROL2 = bowPosition */ -/* CONTROL3 = vibrFreq */ -/* MOD_WHEEL= vibrGain */ -/* */ -/******************************************/ - -#include "Bowed.h" -#include "SKINI11.msg" - -Bowed :: Bowed(MY_FLOAT lowestFreq) -{ - long length; - length = (long) (SRATE / lowestFreq + 1); - neckDelay = new DLineL(length); - length >>= 1; - bridgeDelay = new DLineL(length); - bowTabl = new BowTabl; - reflFilt = new OnePole; - bodyFilt = new BiQuad; - vibr = new RawLoop("rawwaves/sinewave.raw"); - adsr = new ADSR; - vibrGain = (MY_FLOAT) 0.0; - - neckDelay->setDelay((MY_FLOAT) 100.0); - bridgeDelay->setDelay((MY_FLOAT) 29.0); - - bowTabl->setSlope((MY_FLOAT) 3.0); - - reflFilt->setPole((MY_FLOAT) (0.6 - (0.1 * 22050.0 / SRATE))); - reflFilt->setGain((MY_FLOAT) 0.95); - - bodyFilt->setFreqAndReson((MY_FLOAT) 500.0, (MY_FLOAT) 0.85); - bodyFilt->setEqualGainZeroes(); - bodyFilt->setGain((MY_FLOAT) 0.2); - - vibr->normalize(); - vibr->setFreq((MY_FLOAT) 6.12723); - - adsr->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.005,(MY_FLOAT) 0.9,(MY_FLOAT) 0.01); - - betaRatio = (MY_FLOAT) 0.127236; -} - -Bowed :: ~Bowed() -{ - delete neckDelay; - delete bridgeDelay; - delete bowTabl; - delete reflFilt; - delete bodyFilt; - delete vibr; - delete adsr; -} - -void Bowed :: clear() -{ - neckDelay->clear(); - bridgeDelay->clear(); -} - -void Bowed :: setFreq(MY_FLOAT frequency) -{ - baseDelay = SRATE / frequency - (MY_FLOAT) 4.0; /* delay - approx. filter delay */ - bridgeDelay->setDelay(baseDelay * betaRatio); /* bow to bridge length */ - neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); /* bow to nut (finger) length */ -} - -void Bowed :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate) -{ - adsr->setRate(rate); - adsr->keyOn(); - maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.2 * amplitude); -} - -void Bowed :: stopBowing(MY_FLOAT rate) -{ - adsr->setRate(rate); - adsr->keyOff(); -} - -void Bowed :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - this->startBowing(amp,amp * (MY_FLOAT) 0.001); - this->setFreq(freq); -#if defined(_debug_) - printf("Bowed : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - -void Bowed :: noteOff(MY_FLOAT amp) -{ - this->stopBowing(((MY_FLOAT) 1.0 - amp) * (MY_FLOAT) 0.005); -#if defined(_debug_) - printf("Bowed : NoteOff: Amp=%lf\n",amp); -#endif -} - -void Bowed :: setVibrato(MY_FLOAT amount) -{ - vibrGain = amount; -} - -MY_FLOAT Bowed :: tick() -{ - MY_FLOAT bowVelocity; - MY_FLOAT bridgeRefl=(MY_FLOAT) 0,nutRefl=(MY_FLOAT) 0; - MY_FLOAT newVel=(MY_FLOAT) 0,velDiff=(MY_FLOAT) 0,stringVel=(MY_FLOAT) 0; - - bowVelocity = maxVelocity * adsr->tick(); - - bridgeRefl = -reflFilt->tick( - bridgeDelay->lastOut()); /* Bridge Reflection */ - nutRefl = -neckDelay->lastOut(); /* Nut Reflection */ - stringVel = bridgeRefl + nutRefl; /* Sum is String Velocity */ - velDiff = bowVelocity - stringVel; /* Differential Velocity */ - newVel = velDiff * bowTabl->lookup(velDiff); /* Non-Lin Bow Function */ - neckDelay->tick(bridgeRefl + newVel); /* Do string */ - bridgeDelay->tick(nutRefl + newVel); /* propagations */ - - if (vibrGain > 0.0) { - neckDelay->setDelay((baseDelay * ((MY_FLOAT) 1.0 - betaRatio)) + - (baseDelay * vibrGain*vibr->tick())); - } - - lastOutput = bodyFilt->tick(bridgeDelay->lastOut()); - - return lastOutput; -} - -void Bowed :: controlChange(int number, MY_FLOAT value) -{ -#if defined(_debug_) - printf("Bowed : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_BowPressure_) - bowTabl->setSlope((MY_FLOAT) 5.0 - ((MY_FLOAT) 4.0 * value * NORM_7)); - else if (number == __SK_BowPosition_) { - betaRatio = (MY_FLOAT) 0.027236 + ((MY_FLOAT) 0.2 * value * NORM_7); - bridgeDelay->setDelay(baseDelay * betaRatio); /* bow to bridge length */ - neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); /* bow to nut (finger) length */ - } - else if (number == __SK_ModFrequency_) - vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); - else if (number == __SK_ModWheel_) - vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4); - else if (number == __SK_AfterTouch_Cont_) - adsr->setTarget(value * NORM_7); - else { - printf("Bowed : Undefined Control Number!!\n"); - } -} diff --git a/Bowed.h b/Bowed.h deleted file mode 100644 index 92d2174..0000000 --- a/Bowed.h +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************/ -/* Bowed String model ala Smith */ -/* after McIntyre, Schumacher, Woodhouse */ -/* by Perry Cook, 1995-96 */ -/* */ -/* This is a waveguide model, and thus */ -/* relates to various Stanford Univ. */ -/* and possibly Yamaha and other patents.*/ -/* */ -/* Controls: CONTROL1 = bowPressure */ -/* CONTROL2 = bowPosition */ -/* CONTROL3 = vibrFreq */ -/* MOD_WHEEL= vibrGain */ -/* */ -/******************************************/ - -#if !defined(__Bowed_h) -#define __Bowed_h - -#include "Instrmnt.h" -#include "DLineL.h" -#include "BowTabl.h" -#include "OnePole.h" -#include "BiQuad.h" -#include "RawLoop.h" -#include "ADSR.h" - -class Bowed : public Instrmnt -{ - protected: - DLineL *neckDelay; - DLineL *bridgeDelay; - BowTabl *bowTabl; - OnePole *reflFilt; - BiQuad *bodyFilt; - RawLoop *vibr; - ADSR *adsr; - MY_FLOAT maxVelocity; - MY_FLOAT baseDelay; - MY_FLOAT vibrGain; - MY_FLOAT betaRatio; - public: - Bowed(MY_FLOAT lowestFreq); - ~Bowed(); - void clear(); - void startBowing(MY_FLOAT amplitude,MY_FLOAT rate); - void stopBowing(MY_FLOAT rate); - virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); - virtual void noteOff(MY_FLOAT amp); - virtual void setFreq(MY_FLOAT frequency); - void setVibrato(MY_FLOAT amount); - virtual void controlChange(int number, MY_FLOAT value); - virtual MY_FLOAT tick(); -}; - -#endif diff --git a/Brass.cpp b/Brass.cpp deleted file mode 100644 index 707c7bc..0000000 --- a/Brass.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/******************************************/ -/* Waveguide Brass Instrument Model ala */ -/* Cook (TBone, HosePlayer) */ -/* by Perry R. Cook, 1995-96 */ -/* */ -/* This is a waveguide model, and thus */ -/* relates to various Stanford Univ. */ -/* and possibly Yamaha and other patents.*/ -/* */ -/* Controls: CONTROL1 = lipTension */ -/* CONTROL2 = slideLength */ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ -/******************************************/ - -#include "Brass.h" -#include "SKINI11.msg" - -Brass :: Brass(MY_FLOAT lowestFreq) -{ - length = (long) (SRATE / lowestFreq + 1); - delayLine = new DLineA(length); - lipFilter = new LipFilt; - dcBlock = new DCBlock; - adsr = new ADSR; - adsr->setAllTimes((MY_FLOAT) 0.005, (MY_FLOAT) 0.001, (MY_FLOAT) 1.0, (MY_FLOAT) 0.010); - vibr = new RawLoop("rawwaves/sinewave.raw"); - this->clear(); - - vibr->normalize(); - vibr->setFreq((MY_FLOAT) 6.137); - vibrGain = (MY_FLOAT) 0.05; /* breath periodic vibrato component */ - - maxPressure = (MY_FLOAT) 0.0; -} - -Brass :: ~Brass() -{ - delete delayLine; - delete lipFilter; - delete dcBlock; - delete adsr; - delete vibr; -} - -void Brass :: clear() -{ - delayLine->clear(); - lipFilter->clear(); - dcBlock->clear(); -} - -void Brass :: setFreq(MY_FLOAT frequency) -{ - slideTarget = (SRATE / frequency * (MY_FLOAT) 2.0) + (MY_FLOAT) 3.0; - /* fudge correction for filter delays */ - delayLine->setDelay(slideTarget); /* we'll play a harmonic */ - lipTarget = frequency; - lipFilter->setFreq(frequency); -} - -void Brass :: setLip(MY_FLOAT frequency) -{ - lipFilter->setFreq(frequency); -} - -void Brass :: startBlowing(MY_FLOAT amplitude,MY_FLOAT rate) -{ - adsr->setAttackRate(rate); - maxPressure = amplitude; - adsr->keyOn(); -} - -void Brass :: stopBlowing(MY_FLOAT rate) -{ - adsr->setReleaseRate(rate); - adsr->keyOff(); -} - -void Brass :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - this->setFreq(freq); - this->startBlowing(amp, amp * (MY_FLOAT) 0.001); -#if defined(_debug_) - printf("Brass : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - -void Brass :: noteOff(MY_FLOAT amp) -{ - this->stopBlowing(amp * (MY_FLOAT) 0.005); -#if defined(_debug_) - printf("Brass : NoteOff: Amp=%lf\n",amp); -#endif -} - -MY_FLOAT Brass :: tick() -{ - MY_FLOAT breathPressure; - - breathPressure = maxPressure * adsr->tick(); - breathPressure += vibrGain * vibr->tick(); - lastOutput = delayLine->tick( /* bore delay */ - dcBlock->tick( /* block DC */ - lipFilter->tick((MY_FLOAT) 0.3 * breathPressure, /* mouth input */ - (MY_FLOAT) 0.85 * delayLine->lastOut()))); /* and bore reflection */ - return lastOutput; -} - -void Brass :: controlChange(int number, MY_FLOAT value) -{ - MY_FLOAT temp; -#if defined(_debug_) - printf("Brass : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_LipTension_) { - temp = lipTarget * (MY_FLOAT) pow(4.0,(2.0*value*NORM_7) - 1.0); - this->setLip(temp); - } - else if (number == __SK_SlideLength_) - delayLine->setDelay(slideTarget * ((MY_FLOAT) 0.5 + (value * NORM_7))); - else if (number == __SK_ModFrequency_) - vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); - else if (number == __SK_ModWheel_ ) - vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4); - else if (number == __SK_AfterTouch_Cont_) - adsr->setTarget(value * NORM_7); - else { - printf("Brass : Undefined Control Number!!\n"); - } -} diff --git a/DLineA.cpp b/DLineA.cpp deleted file mode 100644 index 5c8f631..0000000 --- a/DLineA.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/*******************************************/ -/* */ -/* AllPass Interpolating Delay Line */ -/* Object by Perry R. Cook 1995-96 */ -/* This one uses a delay line of maximum */ -/* length specified on creation, and */ -/* interpolates fractional length using */ -/* an all-pass filter. This version is */ -/* more efficient for computing static */ -/* length delay lines (alpha and coeff */ -/* are computed only when the length */ -/* is set, there probably is a more */ -/* efficient computational form if alpha */ -/* is changed often (each sample)). */ -/* */ -/*******************************************/ - -#include "DLineA.h" - -DLineA :: DLineA(long max_length) -{ - long i; - length = max_length; - inputs = (MY_FLOAT *) malloc(length * MY_FLOAT_SIZE); - for (i=0;iclear(); - inPoint = 0; - outPoint = length >> 1; -} - -DLineA :: ~DLineA() -{ - free(inputs); -} - -void DLineA :: clear() -{ - long i; - for (i=0;iclear(); - outPoint = 0; - inPoint = length >> 1; -} - -DLineL :: ~DLineL() -{ - free(inputs); -} - -void DLineL :: clear() -{ - long i; - for (i=0;iclear(); - this->setDelay(length * (MY_FLOAT) 0.5); - inPoint = 0; - outPoint = 0; -} - -DLineN :: ~DLineN() -{ - free(inputs); -} - -void DLineN :: clear() -{ - long i; - for (i=0;i=length) { // Check for end condition */ - outPoint -= length; - } - return lastOutput; -} - -/************ Test Main Program *****************/ -/* -void main() -{ - DLineN delay(140); - FILE *fd; - MY_FLOAT temp; - short data; - long i; - - fd = fopen("test.raw","wb"); - - delay.setDelay(128); - for (i=0;i<4096;i++) { - if (i%256 != 0) temp = 0.0; else temp = 1.0; - data = (temp + delay.tick(temp)) * 16000.0; - fwrite(&data,2,1,fd); - } - delay.setDelay(64.5); - for (i=0;i<4096;i++) { - if (i%256 != 0) temp = 0.0; else temp = 1.0; - data = (temp + delay.tick(temp)) * 16000.0; - fwrite(&data,2,1,fd); - } - - fclose(fd); -} -*/ diff --git a/DLineN.h b/DLineN.h deleted file mode 100644 index e078e6f..0000000 --- a/DLineN.h +++ /dev/null @@ -1,31 +0,0 @@ -/*******************************************/ -/* Non-Interpolating Delay Line */ -/* Object by Perry R. Cook 1995-96 */ -/* This one uses a delay line of maximum */ -/* length specified on creation. A non- */ -/* interpolating delay line should be */ -/* used in non-time varying (reverb) or */ -/* non-critical (????) applications. */ -/*******************************************/ - -#if !defined(__DLineN_h) -#define __DLineN_h - -#include "Filter.h" - -class DLineN : public Filter -{ - protected: - long inPoint; - long outPoint; - long length; - public: - DLineN(long max_length); - ~DLineN(); - void clear(); - void setDelay(MY_FLOAT length); - MY_FLOAT tick(MY_FLOAT sample); -}; - -#endif - diff --git a/FM4Op.cpp b/FM4Op.cpp deleted file mode 100644 index 82b4292..0000000 --- a/FM4Op.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/*******************************************/ -/* Master Class for 4 Operator FM Synth */ -/* by Perry R. Cook, 1995-96 */ -/* This instrument contains 4 waves, */ -/* 4 adsr, and various state vars. */ -/* */ -/* The basic Chowning/Stanford FM patent */ -/* expired April 1995, but there exist */ -/* follow-on patents, mostly assigned to */ -/* Yamaha. If you are of the type who */ -/* should worry about this (making money) */ -/* worry away. */ -/* */ -/*******************************************/ - -#include "FM4Op.h" -#include "SKINI11.msg" - -FM4Op :: FM4Op() -{ - int i; - MY_FLOAT temp; - MY_FLOAT tempCoeffs[2] = {(MY_FLOAT) 0.0, (MY_FLOAT) -1.0}; - adsr[0] = new ADSR; - adsr[1] = new ADSR; - adsr[2] = new ADSR; - adsr[3] = new ADSR; - twozero = new TwoZero; - vibWave = new RawLoop("rawwaves/sinewave.raw"); - vibWave->normalize(); - vibWave->setFreq((MY_FLOAT) 6.0); /* should make this random?? */ - modDepth = (MY_FLOAT) 0.0; - /* We don't make the waves here yet, because */ - /* we don't know what they will be. */ - baseFreq = (MY_FLOAT) 440.0; - ratios[0] = (MY_FLOAT) 1.0; - ratios[1] = (MY_FLOAT) 1.0; - ratios[2] = (MY_FLOAT) 1.0; - ratios[3] = (MY_FLOAT) 1.0; - gains[0] = (MY_FLOAT) 1.0; - gains[1] = (MY_FLOAT) 1.0; - gains[2] = (MY_FLOAT) 1.0; - gains[3] = (MY_FLOAT) 1.0; - twozero->setZeroCoeffs(tempCoeffs); - twozero->setGain((MY_FLOAT) 0.0); - control1 = (MY_FLOAT) 1.0; - control2 = (MY_FLOAT) 1.0; - temp = (MY_FLOAT) 1.0; - for (i=99;i>=0;i--) { - __FM4Op_gains[i] = temp; - temp *= (MY_FLOAT) 0.933033; - } - temp = (MY_FLOAT) 1.0; - for (i=15;i>=0;i--) { - __FM4Op_susLevels[i] = temp; - temp *= (MY_FLOAT) 0.707101; - } - temp = (MY_FLOAT) 8.498186; - for (i=0;i<32;i++) { - __FM4Op_attTimes[i] = temp; - temp *= (MY_FLOAT) 0.707101; - } -} - -FM4Op :: ~FM4Op() -{ - delete adsr[0]; - delete adsr[1]; - delete adsr[2]; - delete adsr[3]; - delete waves[0]; - delete waves[1]; - delete waves[2]; - delete waves[3]; - delete vibWave; - delete twozero; -} - -void FM4Op :: loadWaves(char* wave1, char* wave2, char* wave3, char* wave4) -{ - int i; - waves[0] = new RawLoop(wave1); - waves[1] = new RawLoop(wave2); - waves[2] = new RawLoop(wave3); - waves[3] = new RawLoop(wave4); - for (i=0;i<4;i++) { - waves[i]->normalize(); - } -} -void FM4Op :: setFreq(MY_FLOAT frequency) -{ - baseFreq = frequency; - waves[0]->setFreq(baseFreq * ratios[0]); - waves[1]->setFreq(baseFreq * ratios[1]); - waves[2]->setFreq(baseFreq * ratios[2]); - waves[3]->setFreq(baseFreq * ratios[3]); -} - -void FM4Op :: setRatio(int whichOne, MY_FLOAT ratio) -{ - ratios[whichOne] = ratio; - if (ratio>0.0) - waves[whichOne]->setFreq(baseFreq * ratio); - else - waves[whichOne]->setFreq(ratio); -} - -void FM4Op :: setGain(int whichOne, MY_FLOAT gain) -{ - gains[whichOne]=gain; -} - -void FM4Op :: keyOn() -{ - adsr[0]->keyOn(); - adsr[1]->keyOn(); - adsr[2]->keyOn(); - adsr[3]->keyOn(); -} - -void FM4Op :: keyOff() -{ - adsr[0]->keyOff(); - adsr[1]->keyOff(); - adsr[2]->keyOff(); - adsr[3]->keyOff(); -} - -void FM4Op :: noteOff(MY_FLOAT amp) -{ - this->keyOff(); -#if defined(_debug_) - printf("FM4Op : NoteOff: Amp=%lf\n",amp); -#endif -} - -void FM4Op :: setModulationSpeed(MY_FLOAT mSpeed) -{ - vibWave->setFreq(mSpeed); -} - -void FM4Op :: setModulationDepth(MY_FLOAT mDepth) -{ - modDepth = mDepth; -} - -void FM4Op :: setControl1(MY_FLOAT cVal) -{ - control1 = cVal * (MY_FLOAT) 2.0; -} - -void FM4Op :: setControl2(MY_FLOAT cVal) -{ - control2 = cVal * (MY_FLOAT) 2.0; -} - -void FM4Op :: controlChange(int number, MY_FLOAT value) -{ -#if defined(_debug_) - printf("FM4Op : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_Breath_) - this->setControl1(value * NORM_7); - else if (number == __SK_FootControl_) - this->setControl2(value * NORM_7); - else if (number == __SK_ModFrequency_) - this->setModulationSpeed(value * NORM_7 * (MY_FLOAT) 12.0); /* 0 to 12 Hz */ - else if (number == __SK_ModWheel_) - this->setModulationDepth(value * NORM_7); - else if (number == __SK_AfterTouch_Cont_) { - adsr[0]->setTarget(value * NORM_7); - adsr[1]->setTarget(value * NORM_7); - adsr[2]->setTarget(value * NORM_7); - adsr[3]->setTarget(value * NORM_7); - } - else { - printf("FM4Op : Undefined Control Number!!\n"); - } -} - diff --git a/FM4Op.h b/FM4Op.h deleted file mode 100644 index b9dc870..0000000 --- a/FM4Op.h +++ /dev/null @@ -1,59 +0,0 @@ -/*******************************************/ -/* Master Class for 4 Operator FM Synth */ -/* by Perry R. Cook, 1995-96 */ -/* This instrument contains an 4 waves, */ -/* 4 envelopes, and various state vars. */ -/* */ -/* The basic Chowning/Stanford FM patent */ -/* expired April 1995, but there exist */ -/* follow-on patents, mostly assigned to */ -/* Yamaha. If you are of the type who */ -/* should worry about this (making money) */ -/* worry away. */ -/* */ -/*******************************************/ - -#if !defined(__FM4Op_h) -#define __FM4Op_h - -#include "Instrmnt.h" -#include "ADSR.h" -#include "RawLoop.h" -#include "TwoZero.h" - -class FM4Op : public Instrmnt -{ - protected: - ADSR *adsr[4]; - RawLoop *waves[4]; - RawLoop *vibWave; - TwoZero *twozero; - MY_FLOAT baseFreq; - MY_FLOAT ratios[4]; - MY_FLOAT gains[4]; - MY_FLOAT modDepth; - MY_FLOAT control1; - MY_FLOAT control2; - MY_FLOAT __FM4Op_gains[100]; - MY_FLOAT __FM4Op_susLevels[16]; - MY_FLOAT __FM4Op_attTimes[32]; - public: - FM4Op(); - virtual ~FM4Op(); - void loadWaves(char* wave1, char* wave2, char* wave3, char* wave4); - void clear(); - void setFreq(MY_FLOAT frequency); - void setRatio(int whichOne, MY_FLOAT ratio); - void setGain(int whichOne, MY_FLOAT gain); - void keyOn(); - void keyOff(); - void noteOff(MY_FLOAT amp); - /* There's no tick() method here, because that depends on the algorithm */ - void setModulationSpeed(MY_FLOAT mSpeed); - void setModulationDepth(MY_FLOAT mDepth); - void setControl1(MY_FLOAT cVal); - void setControl2(MY_FLOAT cVal); - virtual void controlChange(int number, MY_FLOAT value); -}; - -#endif diff --git a/Flute.cpp b/Flute.cpp deleted file mode 100644 index 023b8af..0000000 --- a/Flute.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/******************************************/ -/* WaveGuide Flute ala Karjalainen, */ -/* Smith, Waryznyk, etc. */ -/* with polynomial Jet ala Cook */ -/* by Perry Cook, 1995-96 */ -/* */ -/* This is a waveguide model, and thus */ -/* relates to various Stanford Univ. */ -/* and possibly Yamaha and other patents.*/ -/* */ -/* Controls: CONTROL1 = jetDelay */ -/* CONTROL2 = noiseGain */ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ -/******************************************/ - -#include "Flute.h" -#include "SKINI11.msg" - -Flute :: Flute(MY_FLOAT lowestFreq) -{ - long length; - length = (long) (SRATE / lowestFreq + 1); - boreDelay = new DLineL(length); - length >>= 1; - jetDelay = new DLineL(length); - jetTable = new JetTabl; - filter = new OnePole; - dcBlock = new DCBlock; - noise = new Noise; - adsr = new ADSR; - vibr = new RawLoop("rawwaves/sinewave.raw"); - this->clear(); - - boreDelay->setDelay((MY_FLOAT) 100.0); - jetDelay->setDelay((MY_FLOAT) 49.0); - - filter->setPole((MY_FLOAT) 0.7 - ((MY_FLOAT) 0.1 * (MY_FLOAT) 22050.0 / SRATE)); - filter->setGain((MY_FLOAT) -1.0); - vibr->normalize(); - vibr->setFreq((MY_FLOAT) 5.925); - adsr->setAllTimes((MY_FLOAT) 0.005, (MY_FLOAT) 0.01, (MY_FLOAT) 0.8, (MY_FLOAT) 0.010); - endRefl = (MY_FLOAT) 0.5; - jetRefl = (MY_FLOAT) 0.5; - noiseGain = (MY_FLOAT) 0.15; /* Breath pressure random component */ - vibrGain = (MY_FLOAT) 0.05; /* breath periodic vibrato component */ - jetRatio = (MY_FLOAT) 0.32; - - maxPressure = (MY_FLOAT) 0.0; -} - -Flute :: ~Flute() -{ - delete jetDelay; - delete boreDelay; - delete jetTable; - delete filter; - delete dcBlock; - delete noise; - delete adsr; - delete vibr; -} - -void Flute :: clear() -{ - jetDelay->clear(); - boreDelay->clear(); - filter->clear(); - dcBlock->clear(); -/* adsr->reset(); */ -} - -void Flute :: setFreq(MY_FLOAT frequency) -{ - MY_FLOAT temp; - lastFreq = frequency * (MY_FLOAT) 0.66666; /* we're overblowing here */ - temp = SRATE / lastFreq - (MY_FLOAT) 2.0; /* Length - approx. filter delay */ - boreDelay->setDelay(temp); /* Length of bore tube */ - jetDelay->setDelay(temp * jetRatio); /* jet delay shorter */ -} - -void Flute :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) -{ - adsr->setAttackRate(rate); - maxPressure = amplitude / (MY_FLOAT) 0.8; - adsr->keyOn(); -} - -void Flute :: stopBlowing(MY_FLOAT rate) -{ - adsr->setReleaseRate(rate); - adsr->keyOff(); -} - -void Flute :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - this->setFreq(freq); - this->startBlowing((MY_FLOAT) 1.1 + (amp * (MY_FLOAT) 0.20),amp * (MY_FLOAT) 0.02); - outputGain = amp + (MY_FLOAT) 0.001; -#if defined(_debug_) - printf("Flute : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - -void Flute :: noteOff(MY_FLOAT amp) -{ - this->stopBlowing(amp * (MY_FLOAT) 0.02); -#if defined(_debug_) - printf("Flute : NoteOff: Amp=%lf\n",amp); -#endif -} - -void Flute :: setJetRefl(MY_FLOAT refl) -{ - jetRefl = refl; -} - -void Flute :: setEndRefl(MY_FLOAT refl) -{ - endRefl = refl; -} - -void Flute :: setJetDelay(MY_FLOAT aRatio) -{ - MY_FLOAT temp; - temp = SRATE / lastFreq - (MY_FLOAT) 2.0; /* Length - approx. filter delay */ - jetRatio = aRatio; - jetDelay->setDelay(temp * aRatio); /* Scaled by ratio */ -} - -MY_FLOAT Flute :: tick() -{ - MY_FLOAT temp; - MY_FLOAT pressureDiff; - MY_FLOAT randPressure; - MY_FLOAT breathPressure; - - breathPressure = maxPressure * adsr->tick(); /* Breath Pressure */ - randPressure = noiseGain * noise->tick(); /* Random Deviation */ - randPressure += vibrGain * vibr->tick(); /* + breath vibrato */ - randPressure *= breathPressure; /* All scaled by Breath Pressure */ - - temp = filter->tick(boreDelay->lastOut()); - temp = dcBlock->tick(temp); /* Block DC on reflection */ - pressureDiff = breathPressure + randPressure - /* Breath Pressure */ - (jetRefl * temp); /* - reflected */ - pressureDiff = jetDelay->tick(pressureDiff); /* Jet Delay Line */ - pressureDiff = jetTable->lookup(pressureDiff) /* Non-Lin Jet + reflected */ - + (endRefl * temp); - lastOutput = (MY_FLOAT) 0.3 * boreDelay->tick(pressureDiff); /* Bore Delay and "bell" filter */ - - lastOutput *= outputGain; - return lastOutput; - -} - -void Flute :: controlChange(int number, MY_FLOAT value) -{ -#if defined(_debug_) - printf("Flute : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_JetDelay_) - this->setJetDelay((MY_FLOAT) 0.08 + ((MY_FLOAT) 0.48 * value * NORM_7)); - else if (number == __SK_NoiseLevel_) - noiseGain = (value * NORM_7 * (MY_FLOAT) 0.4); - else if (number == __SK_ModFrequency_) - vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); - else if (number == __SK_ModWheel_) - vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4); - else if (number == __SK_AfterTouch_Cont_) - adsr->setTarget(value * NORM_7); - else { - printf("Flute : Undefined Control Number!!\n"); - } -} diff --git a/Flute.h b/Flute.h deleted file mode 100644 index 3f9b00a..0000000 --- a/Flute.h +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************/ -/* WaveGuide Flute ala Karjalainen, */ -/* Smith, Waryznyk, etc. */ -/* with polynomial Jet ala Cook */ -/* by Perry Cook, 1995-96 */ -/* */ -/* This is a waveguide model, and thus */ -/* relates to various Stanford Univ. */ -/* and possibly Yamaha and other patents.*/ -/* */ -/* Controls: CONTROL1 = jetDelay */ -/* CONTROL2 = noiseGain */ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ -/******************************************/ - -#if !defined(__Flute_h) -#define __Flute_h - -#include "Instrmnt.h" -#include "JetTabl.h" -#include "DLineL.h" -#include "OnePole.h" -#include "DCBlock.h" -#include "Noise.h" -#include "ADSR.h" -#include "RawLoop.h" - -class Flute : public Instrmnt -{ - protected: - DLineL *jetDelay; - DLineL *boreDelay; - JetTabl *jetTable; - OnePole *filter; - DCBlock *dcBlock; - Noise *noise; - ADSR *adsr; - RawLoop *vibr; - MY_FLOAT lastFreq; - MY_FLOAT maxPressure; - MY_FLOAT jetRefl; - MY_FLOAT endRefl; - MY_FLOAT noiseGain; - MY_FLOAT vibrGain; - MY_FLOAT outputGain; - MY_FLOAT jetRatio; - public: - Flute(MY_FLOAT lowestFreq); - ~Flute(); - void clear(); - void startBlowing(MY_FLOAT amplitude,MY_FLOAT rate); - void stopBlowing(MY_FLOAT rate); - virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); - virtual void noteOff(MY_FLOAT amp); - void setJetRefl(MY_FLOAT refl); - void setEndRefl(MY_FLOAT refl); - virtual void setFreq(MY_FLOAT frequency); - virtual MY_FLOAT tick(); - virtual void controlChange(int number, MY_FLOAT value); - void setJetDelay(MY_FLOAT aLength); -}; - -#endif diff --git a/FormSwep.cpp b/FormSwep.cpp deleted file mode 100644 index 0ab6750..0000000 --- a/FormSwep.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/*******************************************/ -/* Sweepable Formant (2-pole) */ -/* Filter Class, by Perry R. Cook, 1995-96*/ -/* See books on filters to understand */ -/* more about how this works. This drives*/ -/* to a target at speed set by rate. */ -/*******************************************/ - -#include "FormSwep.h" - -FormSwep :: FormSwep() : Filter() -{ - outputs = (MY_FLOAT *) malloc(2 * MY_FLOAT_SIZE); - poleCoeffs[0] = (MY_FLOAT) 0.0; - poleCoeffs[1] = (MY_FLOAT) 0.0; - gain = (MY_FLOAT) 1.0; - freq = (MY_FLOAT) 0.0; - reson = (MY_FLOAT) 0.0; - currentGain = (MY_FLOAT) 1.0; - currentFreq = (MY_FLOAT) 0.0; - currentReson = (MY_FLOAT) 0.0; - targetGain = (MY_FLOAT) 1.0; - targetFreq = (MY_FLOAT) 0.0; - targetReson = (MY_FLOAT) 0.0; - deltaGain = (MY_FLOAT) 0.0; - deltaFreq = (MY_FLOAT) 0.0; - deltaReson = (MY_FLOAT) 0.0; - sweepState = (MY_FLOAT) 0.0; - sweepRate = (MY_FLOAT) 0.002; - dirty = 0; - this->clear(); -} - -FormSwep :: ~FormSwep() -{ - free(outputs); -} - -void FormSwep :: clear() -{ - outputs[0] = (MY_FLOAT) 0.0; - outputs[1] = (MY_FLOAT) 0.0; -} - -void FormSwep :: setPoleCoeffs(MY_FLOAT *coeffs) -{ - dirty = 0; - poleCoeffs[0] = coeffs[0]; - poleCoeffs[1] = coeffs[1]; -} - -void FormSwep :: setFreqAndReson(MY_FLOAT aFreq, MY_FLOAT aReson) -{ - dirty = 0; - reson = aReson; - freq = aFreq; - currentReson = aReson; - currentFreq = aFreq; - poleCoeffs[1] = - (reson * reson); - poleCoeffs[0] = (MY_FLOAT) 2.0 * reson * (MY_FLOAT) cos(TWO_PI * freq / SRATE); -} - -void FormSwep :: setStates(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain) -{ - dirty = 0; - freq = aFreq; - reson = aReson; - gain = aGain; - targetFreq = aFreq; - targetReson = aReson; - targetGain = aGain; - currentFreq = aFreq; - currentReson = aReson; - currentGain = aGain; -} - -void FormSwep :: setTargets(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain) -{ - dirty = 1; - targetFreq = aFreq; - targetReson = aReson; - targetGain = aGain; - deltaFreq = aFreq - currentFreq; - deltaReson = aReson - currentReson; - deltaGain = aGain - currentGain; - sweepState = (MY_FLOAT) 0.0; -} - -void FormSwep :: setSweepRate(MY_FLOAT aRate) -{ - sweepRate = aRate; -} - -void FormSwep :: setSweepTime(MY_FLOAT aTime) -{ - sweepRate = ONE_OVER_SRATE / aTime; -} - -void FormSwep :: setGain(MY_FLOAT aValue) -{ - gain = aValue; -} - -MY_FLOAT FormSwep :: tick(MY_FLOAT sample) /* Perform Filter Operation */ -{ - MY_FLOAT temp; - - if (dirty) { - sweepState += sweepRate; - if (sweepState>= 1.0) { - sweepState = (MY_FLOAT) 1.0; - dirty = 0; - currentReson = targetReson; - reson = targetReson; - currentFreq = targetFreq; - freq = targetFreq; - currentGain = targetGain; - gain = targetGain; - } - else { - currentReson = reson + (deltaReson * sweepState); - currentFreq = freq + (deltaFreq * sweepState); - currentGain = gain + (deltaGain * sweepState); - } - poleCoeffs[1] = - (currentReson * currentReson); - poleCoeffs[0] = (MY_FLOAT) 2.0 * currentReson * - (MY_FLOAT) cos(TWO_PI * currentFreq / SRATE); - } - - temp = currentGain * sample; - temp += poleCoeffs[0] * outputs[0]; - temp += poleCoeffs[1] * outputs[1]; - outputs[1] = outputs[0]; - outputs[0] = temp; - lastOutput = outputs[0]; - return lastOutput; -} - -/************ Test Main Program *****************/ -/* - -void main() -{ - FormSwep filter; - FILE *fd; - MY_FLOAT temp; - short data; - long i; - - fd = fopen("test.raw","wb"); - - filter.setTargets(100.0,0.99,0.01); - for (i=0;i<20000;i++) { - if (i%100 != 0) temp = 0.0; else temp = 1.0; - data = filter.tick(temp) * 32000.0; - fwrite(&data,2,1,fd); - } - - filter.setTargets(1000.0,0.99,0.01); - for (i=0;i<20000;i++) { - if (i%100 != 0) temp = 0.0; else temp = 1.0; - data = filter.tick(temp) * 32000.0; - fwrite(&data,2,1,fd); - } - - filter.setTargets(500.0,0.9999,0.001); - for (i=0;i<20000;i++) { - if (i%100 != 0) temp = 0.0; else temp = 1.0; - data = filter.tick(temp) * 32000.0; - fwrite(&data,2,1,fd); - } - - fclose(fd); -} -*/ - diff --git a/FormSwep.h b/FormSwep.h deleted file mode 100644 index a15d6b1..0000000 --- a/FormSwep.h +++ /dev/null @@ -1,46 +0,0 @@ -/*******************************************/ -/* Sweepable Formant (2-pole) */ -/* Filter Class, by Perry R. Cook, 1995-96*/ -/* See books on filters to understand */ -/* more about how this works. Nothing */ -/* out of the ordinary in this version. */ -/*******************************************/ - -#if !defined(__FormSwep_h) -#define __FormSwep_h - -#include "Filter.h" - -class FormSwep : public Filter -{ - protected: - MY_FLOAT poleCoeffs[2]; - MY_FLOAT freq; - MY_FLOAT reson; - int dirty; - MY_FLOAT targetFreq; - MY_FLOAT targetReson; - MY_FLOAT targetGain; - MY_FLOAT currentFreq; - MY_FLOAT currentReson; - MY_FLOAT currentGain; - MY_FLOAT deltaFreq; - MY_FLOAT deltaReson; - MY_FLOAT deltaGain; - MY_FLOAT sweepState; - MY_FLOAT sweepRate; - public: - FormSwep(); - ~FormSwep(); - void clear(); - void setPoleCoeffs(MY_FLOAT *coeffs); - void setGain(MY_FLOAT aValue); - void setFreqAndReson(MY_FLOAT aFreq, MY_FLOAT aReson); - void setStates(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain); - void setTargets(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain); - void setSweepRate(MY_FLOAT aRate); - void setSweepTime(MY_FLOAT aTime); - MY_FLOAT tick(MY_FLOAT sample); -}; - -#endif diff --git a/HeavyMtl.cpp b/HeavyMtl.cpp deleted file mode 100644 index ded664c..0000000 --- a/HeavyMtl.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************/ -/* Heavy Metal Synth Subclass */ -/* of Algorithm 3 (TX81Z) Subclass of */ -/* 3 Operator FM Synth */ -/* by Perry R. Cook, 1995-96 */ -/******************************************/ - -#include "HeavyMtl.h" - -HeavyMtl :: HeavyMtl() : FM4Alg3() -{ - this->loadWaves("rawwaves/sinewave.raw", - "rawwaves/twopeaks.raw", - "rawwaves/twopeaks.raw", - "rawwaves/sinewave.raw"); - - this->setRatio(0,(MY_FLOAT) (1.00 * 1.000)); - this->setRatio(1,(MY_FLOAT) (4.00 * 0.999)); - this->setRatio(2,(MY_FLOAT) (3.00 * 1.001)); - this->setRatio(3,(MY_FLOAT) (0.50 * 1.002)); - gains[0] = __FM4Op_gains[92]; - gains[1] = __FM4Op_gains[76]; - gains[2] = __FM4Op_gains[91]; - gains[3] = __FM4Op_gains[68]; - adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.001,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); - adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.010,(MY_FLOAT) 1.0,(MY_FLOAT) 0.50); - adsr[2]->setAllTimes((MY_FLOAT) 0.010,(MY_FLOAT) 0.005,(MY_FLOAT) 1.0,(MY_FLOAT) 0.20); - adsr[3]->setAllTimes((MY_FLOAT) 0.030,(MY_FLOAT) 0.010,(MY_FLOAT) 0.2,(MY_FLOAT) 0.20); - twozero->setGain((MY_FLOAT) 2.0); - vibWave->setFreq((MY_FLOAT) 5.5); - modDepth = (MY_FLOAT) 0.00; -} - -HeavyMtl :: ~HeavyMtl() -{ - -} - -void HeavyMtl :: setFreq(MY_FLOAT frequency) -{ - baseFreq = frequency; - waves[0]->setFreq(baseFreq * ratios[0]); - waves[1]->setFreq(baseFreq * ratios[1]); - waves[2]->setFreq(baseFreq * ratios[2]); - waves[3]->setFreq(baseFreq * ratios[3]); -} - -void HeavyMtl :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - gains[0] = amp * __FM4Op_gains[92]; - gains[1] = amp * __FM4Op_gains[76]; - gains[2] = amp * __FM4Op_gains[91]; - gains[3] = amp * __FM4Op_gains[68]; - this->setFreq(freq); - this->keyOn(); -#if defined(_debug_) - printf("HeavyMtl : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - diff --git a/hierarch.txt b/Hierarchy.txt similarity index 60% rename from hierarch.txt rename to Hierarchy.txt index d46a292..3d00c19 100644 --- a/hierarch.txt +++ b/Hierarchy.txt @@ -1,18 +1,20 @@ -Brief Descriptions of Classes in STK98, ver. 2.01 -A ToolKit of Sound Synthesis Classes - and Instruments in C++ -Perry Cook, 1995-96, free distribution for -academic, instructional, tutorial, etc. purposes. +STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Version 3.0 + +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99. + +STK Classes, Version 3.0 Please read README.txt for more information. <--------Building Blocks---------->|<----------------Instruments------------------> -SourcSink Filters Reverb Non-Lin ModalSyn FM Physical Sampling PhISM - & Formant +SourcSink Filters Reverb Non-Lin Modal & FM Physical Sampling PhISM + Formant Object-----------------------------------Instrmnt----------. | | | | | | | -Envelope| Filter Reverb BowTabl | .------------------|---------------------. +Envelope| Filter Reverb BowTabl | .----------------------------------------. | | | | JetTabl | | | | | | | | | ADSR | OneZero PRCRev ReedTabl| Modal4 | FM4Op---.| | | | Shakers | OnePole JCRev | | | | || | | | | @@ -21,53 +23,55 @@ ADSR | OneZero PRCRev ReedTabl| Modal4 | FM4Op---.| | | | Sh Noise | DCBlock LipFilt AgogoBel| HeavyMtl|| Brass SamplFlt| Cabasa | | BiQuad | || Flute | | Bamboo SubNoise| DlineL .____| .____|| Bowed Moog1 | Water Drops - | DLineA | | || | Tambourine + | DLineA | | || BowedBar | Tambourine ._____| DLineN VoicForm FM4Alg4 ||____. | SleighBells | | FormSwep | | | | Guiro -RawWave | PercFlut| Plucked2 | - | | | | - ._____| .____| Mandolin .____| - | | | | | -RawLoop | FM4Alg5 | DrumSynt - | | | - ._____| Rhodey | - | | Wurley | -Modulatr| TubeBell | - | .____| + WvIn | PoleZero PercFlut| Plucked2 | Wrench + | |____. | | | Coke Can +WavWvIn | | .____| Mandolin .____| Sticks +SndWvIn | TablLook | | | Crunch +RawWvIn | FM4Alg5 | DrumSynt Sand Paper +MatWvIn | | | +RTWvIn | Rhodey | + | Wurley | + ._____| TubeBell | + | | .____| +Modulatr| | | + | FM4Alg6 | ._____| | | - | | FM4Alg6 | -SingWave| | | - | FMVoices| - ._____|_____. | - | | | .____| -VoicMang| WvOut | - | | FM4Alg8 - ._____| WavWvOut | - | | SndWvOut BeeThree -RawWvIn | RTWvOut + | | FMVoices| +SingWave|_____. | + | | .____| + ._____| WvOut | + | | | FM4Alg8 +VoicMang| WavWvOut | + | SndWvOut BeeThree + | RTWvOut | MatWvOut - ._____| + ._____| RawWvOut | -MIDIInpt +MIDIIO -********** Instruments and Algorithms ************** +********** INSTRUMENTS AND ALGORITHMS ************** + Each Class will be listed either with all UGs it uses, or the <> of which it is a flavor. All inherit from Instrmnt, which inherits from Object. Plucked.cpp Basic Plucked String DLineA,OneZero,OnePole,Noise -Plucked2.cpp Not so Basic Pluck DLineL,DlineA,OneZero -Mandolin.cpp My Own Mandolin <> +Plucked2.cpp Not So Basic Pluck DLineL,DlineA,OneZero +Mandolin.cpp My Own Mandolin <> Bowed.cpp Not Hideous Bowed String DlineL,BowTabl,OnePole,BiQuad,RawWave,ADSR -Brass.cpp Not So Bad Brass Inst. DLineA,LipFilt,DCBlock,ADSR,RawLoop -Clarinet.cpp Pretty Good Clarinet DLineL,ReedTabl,OneZero,Envelope,Noise,RawLoop -Flute.cpp Pretty Good Flute JetTabl,DLineL,OnePole,DCBlock,Noise,ADSR,RawLoop -Modal4.cpp 4 Resonances Envelope,RawWave,RawLoop,BiQuad,OnePole +Brass.cpp Not So Bad Brass Inst. DLineA,LipFilt,DCBlock,ADSR,RawWvIn +Clarinet.cpp Pretty Good Clarinet DLineL,ReedTabl,OneZero,Envelope,Noise,RawWvIn +Flute.cpp Pretty Good Flute JetTabl,DLineL,OnePole,DCBlock,Noise,ADSR,RawWvIn +BowedBar.cpp Pretty Good Bowed Bar DLineN,BowTabl,ADSR,BiQuad +Modal4.cpp 4 Resonances Envelope,RawWvIn,RawWvIn,BiQuad,OnePole Marimba.cpp <> Vibraphn.cpp <> Agogobel.cpp <> -FM4Op.cpp 4 Operator FM Master ADSR,RawLoop,TwoZero +FM4Op.cpp 4 Operator FM Master ADSR,RawWvIn,TwoZero FM4Alg3.cpp 3 Cascade w/ FB Mod. <> FM4Alg4.cpp Like Alg3 but diff. <> FM4Alg5.cpp 2 Parallel Simple FMs <> @@ -80,36 +84,44 @@ Wurley.cpp Wurlitz. Elec. Piano <> TubeBell.cpp Classic FM Bell <> FMVoices.cpp 3-Formant Voice Synth. <> BeeThree.cpp Cheezy Organ for Paul <> -Sampler.cpp Sampling Synth. 4 each ADSR, RawWave (att), RawLoop (loop), OnePole +Sampler.cpp Sampling Synth. 4 each ADSR, RawWvIn (att), RawWvIn (loop), OnePole SamplFlt.cpp Sampler with Swept Filter <> Moog1.cpp Swept filter flavor of <> VoicForm.cpp Source/Filter Voice Envelope,Noise,SingWave,FormSwep,OnePole,OneZero DrumSynt.cpp Drum Synthesizer bunch of RawWvIn, and OnePole Shakers.cpp Stochastic Event Models -*********** Basic Unit Generators ************** +*********** BASIC UNIT GENERATORS ************** Master Object: Object.cpp For compatibility with Objective C -Source&Sink: RawWave.cpp Lin-Interp Wavetable, Looped or 1 Shot - RawLoop.cpp Lin-Interp Wavetable, Looping - RawWvIn.cpp Lin-Interp Wave In streaming 'device' - Envelope.cpp Linearly Goes to Target by Rate, + noteOn/Off +Source&Sink: Envelope.cpp Linearly Goes to Target by Rate, + noteOn/Off ADSR.cpp ADSR Flavor of Envelope Noise.cpp Random Number Generator SubNoise.cpp Random Numbers each N samples +Inputs: TablLook.cpp Lookup Table (assumes given data in big-endian format) + WvIn.cpp Input Master Class (Looping, One-Shot, + Interpolating, Non-Interpolating) + RawWvIn.cpp STK Raw-file Input + SndWvIn.cpp .snd Input Class + WavWvIn.cpp .wav Input Class + MatWvIn.cpp Matlab MAT-file Input Class + RTWvIn.cpp Realtime Input Class + Outputs: WvOut.cpp Output Master Class + RawWvOut.cpp STK Raw-file Output Class SndWvOut.cpp .snd Output Class WavWvOut.cpp .wav Output Class RTWvOut.cpp Realtime Output Class - MatWvOut.cpp Matlab Matfile Output Class + MatWvOut.cpp Matlab MaT-file Output Class -Inputs: MIDIInpt.cpp MIDI Stream Parser Class +MIDI: MIDIIO.cpp MIDI I/O Class Filters: Filter.cpp Filter Master Class OneZero.cpp One Zero Filter OnePole.cpp One Pole Filter + PoleZero.cpp One Pole/One Zero Filter DCBlock.cpp DC Blocking 1Pole/1Zero Filter TwoZero.cpp Two Zero Filter TwoPole.cpp Two Pole Filter @@ -129,5 +141,5 @@ NonLin&Lookup: JetTabl.cpp Cubic Jet NonLinearity ReedTabl.cpp 1 break point Reed NonLinearity LipFilt.cpp Pressure Controlled BiQuad with NonLin -Derived: Modulatr.cpp Per. and Rnd. Vibrato: RawWave,SubNoise,OnePole - SingWave.cpp Looping Wavetable with: Modulatr,Envelope +Derived: Modulatr.cpp Per. and Rnd. Vibrato: RawWave, SubNoise, OnePole + SingWave.cpp Looping Wavetable with: Modulatr, Envelope diff --git a/Makefile.NeXT b/Makefile.NeXT deleted file mode 100644 index e949ed7..0000000 --- a/Makefile.NeXT +++ /dev/null @@ -1,47 +0,0 @@ -# STK98 Makefile - NeXTStep solo version - -O_FILES = Object.o Envelope.o ADSR.o Noise.o SubNoise.o \ - RawWave.o RawShot.o RawLoop.o RawInterp.o \ - Modulatr.o Filter.o OneZero.o \ - OnePole.o TwoZero.o TwoPole.o DCBlock.o \ - BiQuad.o DLineA.o DLineL.o DLineN.o VoicMang.o \ - FormSwep.o BowTabl.o JetTabl.o ReedTabl.o \ - LipFilt.o Modal4.o FM4Op.o FM4Alg3.o FM4Alg4.o \ - FM4Alg5.o FM4Alg6.o FM4Alg8.o Plucked2.o \ - SamplFlt.o Sampler.o SKINI11.o Simple.o \ - SingWave.o VoicForm.o FMVoices.o swapstuf.o \ - \ - Instrmnt.o Marimba.o Vibraphn.o AgogoBel.o Shakers.o \ - Plucked.o Mandolin.o Clarinet.o Flute.o Moog1.o \ - Brass.o Bowed.o Rhodey.o Wurley.o TubeBell.o \ - HeavyMtl.o PercFlut.o BeeThree.o DrumSynt.o \ - \ - WvOut.o SndWvOut.o WavWvOut.o MatWvOut.o \ - Reverb.o PRCRev.o JCRev.o NRev.o - -RM = /bin/rm - -CC = cc -arch m68k -arch i386 -Wall -INSTR = syntmono - -.SUFFIXES: .cpp -.cpp.o: Object.h - $(CC) -c $*.cpp - -all: $(INSTR) - -syntmono: syntmono.cpp $(O_FILES) - $(CC) $(INCLUDE) -o syntmono syntmono.cpp $(O_FILES) $(LIBRARY) - -MD2SKINI: MD2SKINI.cpp $(O_FILES) - $(CC) -o MD2SKINI MD2SKINI.cpp $(O_FILES) $(LIBRARY) - -clean : - rm *.o - rm $(INSTR) - -cleanIns : - rm $(INSTR) - -strip : - strip $(INSTR) diff --git a/Makefile.all b/Makefile.all deleted file mode 100644 index 286e3a4..0000000 --- a/Makefile.all +++ /dev/null @@ -1,68 +0,0 @@ -# STK98 Makefile - Global version for Unix systems which have GNU -# Makefile utilities installed. If this Makefile does not work on -# your system, try using the platform specific Makefiles (.sgi, -# .next, and .linux). - -OS = $(shell uname) - -O_FILES = Object.o Envelope.o ADSR.o Noise.o SubNoise.o \ - RawWave.o RawWvIn.o RawLoop.o \ - Modulatr.o Filter.o OneZero.o \ - OnePole.o TwoZero.o TwoPole.o DCBlock.o \ - BiQuad.o DLineA.o DLineL.o DLineN.o VoicMang.o \ - FormSwep.o BowTabl.o JetTabl.o ReedTabl.o \ - LipFilt.o Modal4.o FM4Op.o FM4Alg3.o FM4Alg4.o \ - FM4Alg5.o FM4Alg6.o FM4Alg8.o Plucked2.o \ - SamplFlt.o Sampler.o SKINI11.o Simple.o \ - SingWave.o VoicForm.o FMVoices.o swapstuf.o \ - \ - Instrmnt.o Marimba.o Vibraphn.o AgogoBel.o Shakers.o \ - Plucked.o Mandolin.o Clarinet.o Flute.o Moog1.o \ - Brass.o Bowed.o Rhodey.o Wurley.o TubeBell.o \ - HeavyMtl.o PercFlut.o BeeThree.o DrumSynt.o \ - \ - WvOut.o SndWvOut.o WavWvOut.o MatWvOut.o \ - Reverb.o PRCRev.o JCRev.o NRev.o - -RM = /bin/rm - -ifeq ($(OS),NEXTSTEP) # These are for NeXT - CC = cc -arch m68k -arch i386 -Wall - INSTR = syntmono -endif - -ifeq ($(OS),IRIX) # These are for SGI - INSTR = MD2SKINI syntmono - CC = CC -O # -g -fullwarn -D__SGI_CC__ - O_FILES += RTWvOut.o RTSoundIO.o MIDIIO.o - LIBRARY = -L/usr/sgitcl/lib -laudio -lmd -lm -endif - -ifeq ($(OS),Linux) # These are for Linux - INSTR = syntmono MD2SKINI - CC = gcc -O3 # -g -pg -O3 - O_FILES += RTWvOut.o RTSoundIO.o MIDIIO.o - LIBRARY = -lpthread -lm -endif - -.SUFFIXES: .cpp -.cpp.o: Object.h - $(CC) -c $*.cpp - -all: $(INSTR) - -syntmono: syntmono.cpp $(O_FILES) - $(CC) $(INCLUDE) -o syntmono syntmono.cpp $(O_FILES) $(LIBRARY) - -MD2SKINI: MD2SKINI.cpp $(O_FILES) - $(CC) -o MD2SKINI MD2SKINI.cpp $(O_FILES) $(LIBRARY) - -clean : - rm *.o - rm $(INSTR) - -cleanIns : - rm $(INSTR) - -strip : - strip $(INSTR) diff --git a/Makefile.linux b/Makefile.linux deleted file mode 100644 index 387724f..0000000 --- a/Makefile.linux +++ /dev/null @@ -1,49 +0,0 @@ -# STK98 Makefile - Linux solo version - -O_FILES = Object.o Envelope.o ADSR.o Noise.o SubNoise.o \ - RawWave.o RawShot.o RawLoop.o RawInterp.o \ - Modulatr.o Filter.o OneZero.o \ - OnePole.o TwoZero.o TwoPole.o DCBlock.o \ - BiQuad.o DLineA.o DLineL.o DLineN.o VoicMang.o \ - FormSwep.o BowTabl.o JetTabl.o ReedTabl.o \ - LipFilt.o Modal4.o FM4Op.o FM4Alg3.o FM4Alg4.o \ - FM4Alg5.o FM4Alg6.o FM4Alg8.o Plucked2.o \ - SamplFlt.o Sampler.o SKINI11.o Simple.o \ - SingWave.o VoicForm.o FMVoices.o swapstuf.o \ - \ - Instrmnt.o Marimba.o Vibraphn.o AgogoBel.o Shakers.o \ - Plucked.o Mandolin.o Clarinet.o Flute.o Moog1.o \ - Brass.o Bowed.o Rhodey.o Wurley.o TubeBell.o \ - HeavyMtl.o PercFlut.o BeeThree.o DrumSynt.o \ - \ - WvOut.o SndWvOut.o WavWvOut.o MatWvOut.o \ - Reverb.o PRCRev.o JCRev.o NRev.o \ - RTWvOut.o RTSoundIO.o MIDIIO.o - -RM = /bin/rm - -INSTR = syntmono MD2SKINI -CC = gcc -O3 # -g -pg -O3 -LIBRARY = -lpthread -lm - -.SUFFIXES: .cpp -.cpp.o: Object.h - $(CC) -c $*.cpp - -all: $(INSTR) - -syntmono: syntmono.cpp $(O_FILES) - $(CC) $(INCLUDE) -o syntmono syntmono.cpp $(O_FILES) $(LIBRARY) - -MD2SKINI: MD2SKINI.cpp $(O_FILES) - $(CC) -o MD2SKINI MD2SKINI.cpp $(O_FILES) $(LIBRARY) - -clean : - rm *.o - rm $(INSTR) - -cleanIns : - rm $(INSTR) - -strip : - strip $(INSTR) diff --git a/Makefile.sgi b/Makefile.sgi deleted file mode 100644 index ad5d647..0000000 --- a/Makefile.sgi +++ /dev/null @@ -1,49 +0,0 @@ -# STK98 Makefile - SGI solo version - -O_FILES = Object.o Envelope.o ADSR.o Noise.o SubNoise.o \ - RawWave.o RawShot.o RawLoop.o RawInterp.o \ - Modulatr.o Filter.o OneZero.o \ - OnePole.o TwoZero.o TwoPole.o DCBlock.o \ - BiQuad.o DLineA.o DLineL.o DLineN.o VoicMang.o \ - FormSwep.o BowTabl.o JetTabl.o ReedTabl.o \ - LipFilt.o Modal4.o FM4Op.o FM4Alg3.o FM4Alg4.o \ - FM4Alg5.o FM4Alg6.o FM4Alg8.o Plucked2.o \ - SamplFlt.o Sampler.o SKINI11.o Simple.o \ - SingWave.o VoicForm.o FMVoices.o swapstuf.o \ - \ - Instrmnt.o Marimba.o Vibraphn.o AgogoBel.o Shakers.o \ - Plucked.o Mandolin.o Clarinet.o Flute.o Moog1.o \ - Brass.o Bowed.o Rhodey.o Wurley.o TubeBell.o \ - HeavyMtl.o PercFlut.o BeeThree.o DrumSynt.o \ - \ - WvOut.o SndWvOut.o WavWvOut.o MatWvOut.o \ - Reverb.o PRCRev.o JCRev.o NRev.o \ - RTWvOut.o RTSoundIO.o MIDIIO.o - -RM = /bin/rm - -INSTR = MD2SKINI syntmono -CC = CC -O # -g -fullwarn -D__SGI_CC__ -LIBRARY = -L/usr/sgitcl/lib -laudio -lmd -lm - -.SUFFIXES: .cpp -.cpp.o: Object.h - $(CC) -c $*.cpp - -all: $(INSTR) - -syntmono: syntmono.cpp $(O_FILES) - $(CC) $(INCLUDE) -o syntmono syntmono.cpp $(O_FILES) $(LIBRARY) - -MD2SKINI: MD2SKINI.cpp $(O_FILES) - $(CC) -o MD2SKINI MD2SKINI.cpp $(O_FILES) $(LIBRARY) - -clean : - rm *.o - rm $(INSTR) - -cleanIns : - rm $(INSTR) - -strip : - strip $(INSTR) diff --git a/Marimba.cpp b/Marimba.cpp deleted file mode 100644 index eca6d5c..0000000 --- a/Marimba.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/*******************************************/ -/* Marimba SubClass of Modal4 Instrument, */ -/* by Perry R. Cook, 1995-96 */ -/* */ -/* Controls: CONTROL1 = stickHardness */ -/* CONTROL2 = strikePosition*/ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ -/*******************************************/ - -#include "Marimba.h" -#include "SKINI11.msg" - -Marimba :: Marimba() : Modal4() -{ - wave = new RawInterp("rawwaves/marmstk1.raw"); - wave->normalize(); - wave->setRate((MY_FLOAT) 0.5); /* normal stick */ - this->setRatioAndReson(0, (MY_FLOAT) 1.00,(MY_FLOAT) 0.9996); /* Set all 132.0 */ - this->setRatioAndReson(1, (MY_FLOAT) 3.99,(MY_FLOAT) 0.9994); /* of our 523.0 */ - this->setRatioAndReson(2,(MY_FLOAT) 10.65,(MY_FLOAT) 0.9994); /* default 1405.0 */ - this->setRatioAndReson(3,-(MY_FLOAT) 2443.0,(MY_FLOAT) 0.999); /* resonances 2443.0 */ - this->setFiltGain(0,(MY_FLOAT) 0.04); /* and */ - this->setFiltGain(1,(MY_FLOAT) 0.01); /* gains */ - this->setFiltGain(2,(MY_FLOAT) 0.01); /* for each */ - this->setFiltGain(3,(MY_FLOAT) 0.008); /* resonance */ - directGain = (MY_FLOAT) 0.1; - multiStrike = 0; -} - -Marimba :: ~Marimba() -{ - delete wave; -} - -void Marimba :: setStickHardness(MY_FLOAT hardness) -{ - stickHardness = hardness; - wave->setRate((MY_FLOAT) (0.25 * (MY_FLOAT) pow(4.0,stickHardness))); - masterGain = (MY_FLOAT) 0.1 + ((MY_FLOAT) 1.8 * stickHardness); -} - -void Marimba :: setStrikePosition(MY_FLOAT position) -{ - MY_FLOAT temp,temp2; - temp2 = position * PI; - strikePosition = position; /* Hack only first three modes */ - temp = (MY_FLOAT) sin(temp2); - this->setFiltGain(0,(MY_FLOAT) 0.12 * temp); /* 1st mode function of pos. */ - temp = (MY_FLOAT) sin(0.05 + (3.9 * temp2)); - this->setFiltGain(1,(MY_FLOAT) -0.03 * temp); /* 2nd mode function of pos. */ - temp = (MY_FLOAT) sin(-0.05 + (11 * temp2)); - this->setFiltGain(2,(MY_FLOAT) 0.11 * temp); /* 3rd mode function of pos. */ -} - -void Marimba :: setModulationSpeed(MY_FLOAT mSpeed) -{ - /* don't bother here, marimba decay so fast, mod doesn't make sense */ -} - -void Marimba :: setModulationDepth(MY_FLOAT mDepth) -{ -} - -void Marimba :: strike(MY_FLOAT amplitude) -{ - int temp; - temp = rand() >> 10; - if (temp < 2) { - multiStrike = 1; -#if defined(_debug_) - printf("striking twice here!!\n"); -#endif - } - else if (temp < 1) { - multiStrike = 2; -#if defined(_debug_) - printf("striking three times here!!!\n"); -#endif - } - else multiStrike = 0; - Modal4::strike(amplitude); -} - -void Marimba :: controlChange(int number, MY_FLOAT value) -{ -#if defined(_debug_) - printf("Marimba : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_StickHardness_) - this->setStickHardness(value * NORM_7); - else if (number == __SK_StrikePosition_) - this->setStrikePosition(value * NORM_7); - else if (number == __SK_ModFrequency_) - vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); - else if (number == __SK_ModWheel_) - vibrGain = (value * NORM_7); - else if (number == __SK_AfterTouch_Cont_) - this->strike(value * NORM_7); - else { - printf("Marimba : Undefined Control Number!!\n"); - } -} - -MY_FLOAT Marimba :: tick() -{ - if (multiStrike>0) - if (wave->isFinished()) { - wave->reset(); - multiStrike -= 1; - } - return Modal4::tick(); -} diff --git a/MatWvOut.cpp b/MatWvOut.cpp deleted file mode 100644 index d10a946..0000000 --- a/MatWvOut.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/*******************************************/ -/* Matlab MAT File Output Class, */ -/* by Gary P. Scavone, 1998. */ -/* This object creates a Matlab MAT-file */ -/* structure and fills it with buffers of */ -/* samples (doubles). */ -/* */ -/* The Matlab MAT-file format is not */ -/* available to the general public. I */ -/* spent several days reverse-engineering */ -/* the file format to create this class. */ -/* I couldn't figure out what a few of */ -/* the header fields correspond to, but */ -/* for the purposes of STK, this */ -/* shouldn't create any problems. */ -/*******************************************/ - -#include "MatWvOut.h" - -/******** Matlab Matfile Header Struct *******/ -struct matheaderform { - char heading[124]; - short a[2]; - long b[10]; - /* There's more, but it's of variable length */ -}; - -FILE *openMatFile(int chans,char *fileName) { - struct matheaderform hdr; - FILE *fd; - char tempName[128]; - int i, namelen; - long longtmp, headsize; - - strcpy(hdr.heading,"MATLAB 5.0 MAT-file, Generated by STK98. This file format was hacked by Gary P. Scavone, CCRMA, Stanford University, 1998."); - - for (i=strlen(hdr.heading);i<124;i++) hdr.heading[i] = ' '; - - hdr.a[0] = (short) 256; - hdr.a[1] = (short) 'M'; - hdr.a[1] <<= 8; - hdr.a[1] += 'I'; - hdr.b[0] = (long) 14; - hdr.b[1] = (long) 0; /* Size of file after this point to end (in bytes) */ - hdr.b[2] = (long) 6; - hdr.b[3] = (long) 8; - hdr.b[4] = (long) 6; - hdr.b[5] = (long) 0; - hdr.b[6] = (long) 5; - hdr.b[7] = (long) 8; - hdr.b[8] = (long) chans; /* This is the number of rows */ - hdr.b[9] = (long) 0; /* This is the number of columns */ - - strcpy(tempName,fileName); - strcat(tempName,".mat"); - - fd = fopen(tempName,"w+b"); - if (!fd) { - printf("Couldn't create matfile %s !!!!!!!!\n",fileName); - exit(0); - } - - printf("Creating matfile %s.\n", tempName); - fwrite(&hdr,sizeof(char),168,fd); /* Write the fixed portion of the header */ - - /* The next 4 bytes can be viewed as two shorts, but they are byteswapped - as a long. The first short value seems to always be one; the second - short will be the length of the variable name IF IT IS <= 4; if the - variable name length is >4, this short is zero and the length is put - in the next 4 bytes. The variable name length is limited to 31 - characters (32 with a '\n'). The actual variable name then follows. - The variable name is "zero-padded" out to the following minimum - lengths (in bits): 4, 8, 16, 24, 32. - */ - namelen = strlen(fileName); - if (namelen > 31) { /* Check length of variable name (file name) */ - fprintf(stderr, "File name too long ... should be 31 characters or less.\n"); - fclose(fd); - exit(0); - } - - if (namelen > 4) { - longtmp = 1; - fwrite(&longtmp,sizeof(long),1,fd); - fwrite(&namelen,sizeof(long),1,fd); - headsize = 44 + namelen; - } else { - longtmp = namelen; - longtmp <<= 16; - longtmp += 1; - fwrite(&longtmp,sizeof(long),1,fd); - headsize = 40 + namelen; - } - - fwrite(fileName,sizeof(char),namelen,fd); /* Write the variable (file) name */ - if (namelen < 5) - longtmp = 4 - namelen; - else if (namelen < 9) - longtmp = 8 - namelen; - else if (namelen < 17) - longtmp = 16 - namelen; - else if (namelen < 25) - longtmp = 24 - namelen; - else longtmp = 32 - namelen; - - headsize += longtmp + 8; /* Add length (8) of following bytes */ - fseek(fd,longtmp,SEEK_CUR); - - longtmp = 9; - fwrite(&longtmp,sizeof(long),1,fd); - longtmp = 0; /* Size of data in bytes (8 per sample) */ - fwrite(&longtmp,sizeof(long),1,fd); - - fseek(fd,132,SEEK_SET); - fwrite(&headsize,sizeof(long),1,fd); /* Write header size ... will update at end */ - fseek(fd,0,SEEK_END); - - return fd; -} - -MatWvOut :: MatWvOut(char *fileName) -{ - chans = 1; - pan = 0.5; - fd = openMatFile(chans,fileName); - counter = 0; - totalCount = 0; -} - -MatWvOut :: MatWvOut(int channels, char *fileName) -{ - chans = channels; - pan = 0.5; - fd = openMatFile(chans,fileName); - counter = 0; - totalCount = 0; -} - -MatWvOut :: ~MatWvOut() -{ - double temp; - long headsize, temp1; - - fwrite(data,sizeof(double),counter,fd); - temp = (double) totalCount * ONE_OVER_SRATE; - printf("%f Seconds Computed\n",temp); - fseek(fd,164,SEEK_SET); - fwrite(&totalCount,sizeof(long),1,fd); /* Write number of columns */ - fseek(fd,132,SEEK_SET); - fread(&headsize,sizeof(long),1,fd); - temp1 = headsize; - headsize += (long) (totalCount * 8 * 2); - fseek(fd,132,SEEK_SET); - fwrite(&headsize,sizeof(long),1,fd); /* Write file size (minus some header info) */ - fseek(fd,temp1+128,SEEK_SET); - temp1 = totalCount * 8 * 2; - fwrite(&temp1,sizeof(long),1,fd); /* Write data size (in bytes) */ - fclose(fd); -} - -long MatWvOut :: getCounter() -{ - return totalCount; -} - -MY_FLOAT MatWvOut :: getTime() -{ - return (MY_FLOAT) totalCount * ONE_OVER_SRATE; -} - -void MatWvOut :: tick(MY_FLOAT sample) -{ - if (chans==1) { - data[counter++] = (double) (sample); - } - else { - data[counter++] = (double) (sample * (1.0 - pan)); - data[counter++] = (double) (sample * pan); - } - totalCount += 1; - if (counter == MAT_BUFFER_SIZE) { - fwrite(data,sizeof(double),MAT_BUFFER_SIZE,fd); - counter = 0; - } -} - -void MatWvOut :: tick(MY_FLOAT lsamp, MY_FLOAT rsamp) -{ - if (chans==1) { - data[counter++] = (double) (lsamp + rsamp); - } - else { - data[counter++] = (double) (lsamp); - data[counter++] = (double) (rsamp); - } - totalCount += 1; - if (counter == MAT_BUFFER_SIZE) { - fwrite(data,sizeof(double),MAT_BUFFER_SIZE,fd); - counter = 0; - } -} - -void MatWvOut :: setMonoPan(MY_FLOAT aPan) -{ - pan = aPan; - if (aPan < 0.0) { - pan = 0.0; - printf("Pan < 0.0, correcting to 0.0\n"); - } - if (aPan > 1.0) { - pan = 1.0; - printf("Pan > 1.0, correcting to 1.0\n"); - } -} diff --git a/MatWvOut.h b/MatWvOut.h deleted file mode 100644 index 643c517..0000000 --- a/MatWvOut.h +++ /dev/null @@ -1,46 +0,0 @@ -/*******************************************/ -/* Matlab MAT File Output Class, */ -/* by Gary P. Scavone, 1998. */ -/* This object creates a Matlab MAT-file */ -/* structure and fills it with buffers of */ -/* samples (doubles). */ -/* */ -/* The Matlab MAT-file format is not */ -/* available to the general public. I */ -/* spent several days reverse-engineering */ -/* the file format to create this class. */ -/* I couldn't figure out what a few of */ -/* the header fields correspond to, but */ -/* for the purposes of STK, this */ -/* shouldn't create any problems. */ -/*******************************************/ - -#include "Object.h" -#include "WvOut.h" - -#if !defined(__MatWvOut_h) -#define __MatWvOut_h - -#define MAT_BUFFER_SIZE 1024 - -class MatWvOut : public WvOut -{ - protected: - FILE *fd; - double data[MAT_BUFFER_SIZE]; /* not MY_FLOAT because MAT uses doubles */ - long counter; - long totalCount; - int chans; - MY_FLOAT pan; - public: - MatWvOut(char *infileName); - MatWvOut(int channels, char *infileName); - ~MatWvOut(); - long getCounter(); - MY_FLOAT getTime(); - void setMonoPan(MY_FLOAT aPan); - void tick(MY_FLOAT sample); - void tick(MY_FLOAT lsamp, MY_FLOAT rsamp); -}; - -#endif // defined(__MatWvOut_h) diff --git a/Moog1.cpp b/Moog1.cpp deleted file mode 100644 index f8bac75..0000000 --- a/Moog1.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************/ -/* Test Sampler Subclass of */ -/* Sampling Synthesizer Class */ -/* by Perry R. Cook, 1995-96 */ -/* */ -/* Controls: CONTROL1 = filterQ */ -/* CONTROL2 = filterRate */ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ -/******************************************/ - -#include "Moog1.h" -#include "SKINI11.msg" - -Moog1 :: Moog1() : SamplFlt() -{ - attacks[0] = new RawInterp("rawwaves/mandpluk.raw"); - loops[0] = new RawLoop("rawwaves/impuls20.raw"); - loops[1] = new RawLoop("rawwaves/sinewave.raw"); /* Steal one for vibrato */ - attacks[0]->normalize(); - loops[0]->normalize(); - loops[1]->normalize(); - loops[1]->setFreq((MY_FLOAT) 6.122); - adsr->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.5,(MY_FLOAT) 0.6,(MY_FLOAT) 0.250); - filterQ = (MY_FLOAT) 0.85; - filterRate = (MY_FLOAT) 0.0001; - modDepth = (MY_FLOAT) 0.0; -} - -Moog1 :: ~Moog1() -{ - delete attacks[0]; - delete loops[0]; - delete loops[1]; -} - -void Moog1 :: setFreq(MY_FLOAT frequency) -{ - baseFreq = frequency; - attacks[0]->setFreq(baseFreq * (MY_FLOAT) 0.01); - loops[0]->setFreq(baseFreq); -} - -void Moog1 :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - MY_FLOAT temp; - - this->setFreq(freq); - this->keyOn(); - attackGain = amp * (MY_FLOAT) 0.5; - loopGain = amp; - - temp = filterQ + (MY_FLOAT) 0.05; - filters[0]->setStates((MY_FLOAT) 2000.0,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); - filters[1]->setStates((MY_FLOAT) 2000.0,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); - temp = filterQ + (MY_FLOAT) 0.099; - filters[0]->setTargets((MY_FLOAT) freq,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); - filters[1]->setTargets((MY_FLOAT) freq,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); - filters[0]->setSweepRate(filterRate * (MY_FLOAT) 22050.0 / SRATE); - filters[1]->setSweepRate(filterRate * (MY_FLOAT) 22050.0 / SRATE); -#if defined(_debug_) - printf("Moog1 : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - -void Moog1 :: setModulationSpeed(MY_FLOAT mSpeed) -{ - loops[1]->setFreq(mSpeed); -} - -void Moog1 :: setModulationDepth(MY_FLOAT mDepth) -{ - modDepth = mDepth * (MY_FLOAT) 0.5; -} - -void Moog1 :: controlChange(int number, MY_FLOAT value) -{ -#if defined(_debug_) - printf("Moog1 : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_FilterQ_) - filterQ = (MY_FLOAT) 0.80 + ((MY_FLOAT) 0.1 * value * NORM_7); - else if (number == __SK_FilterSweepRate_) - filterRate = (value * NORM_7 * (MY_FLOAT) 0.0002); - else if (number == __SK_ModFrequency_) - this->setModulationSpeed(value * NORM_7 * (MY_FLOAT) 12.0); - else if (number == __SK_ModWheel_) - this->setModulationDepth(value * NORM_7); - else if (number == __SK_AfterTouch_Cont_) - adsr->setTarget(value * NORM_7); - else { - printf("Moog1 : Undefined Control Number!!\n"); - } -} - -MY_FLOAT Moog1 :: tick() -{ - MY_FLOAT temp; - - if (modDepth!=0.0) { - temp = loops[1]->tick() * modDepth; - loops[0]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp)); - } - lastOutput = SamplFlt :: tick(); - return lastOutput; -} - diff --git a/Object.h b/Object.h deleted file mode 100644 index 241984d..0000000 --- a/Object.h +++ /dev/null @@ -1,134 +0,0 @@ -/*********************************************/ -/* Object Class, by Perry R. Cook, 1995-96 */ -/* This is mostly here for compatibility */ -/* with Objective C. We'll also stick */ -/* global defines here, so everyone will */ -/* see them. */ -/*********************************************/ - -#if !defined(__Object_h) -#define __Object_h - -#include -#include -#include -#include - -class Object -{ - public: - protected: - Object(); - virtual ~Object(); -}; - -/* Uncomment your OS type below! */ - -/* #define __OS_NeXT_ */ -#define __OS_IRIX_ -/* #define __OS_Linux_ */ -/* #define __OS_Win_ */ - -#if defined(__OS_NeXT_) /* For NeXTStep - Black or White Hardware */ - #define __NeXT_ -#elif defined(__OS_IRIX_) /* For SGI */ - #define __SGI_REALTIME_ - typedef int bool; -#elif defined(__OS_Linux_) /* For Linux */ - #define __USS_REALTIME_ - #define __LITTLE_ENDIAN__ -#elif defined(__OS_Win_) /* For Windows95 or NT */ - #define __WINDS_REALTIME_ /* For Direct Sound API */ -/* #define __WINMM_REALTIME_ */ /* For Win MM API */ - #define __SOCKET - #define __LITTLE_ENDIAN__ -#endif - -/* Real-time output buffer size. If clicks are occuring in the - * output sound stream, a larger buffer size may help. Larger - * buffer sizes, however, produce more latency between input and - * output. -*/ -#define RT_BUFFER_SIZE 256 - -/* This sets the maximum number of simultaneous - * (within a buffer) MIDI messages that can be - * serviced before messages begin to be lost or - * overwritten. It should be a function of - * RT_BUFFER_SIZE - */ -#define MAX_IN_STRINGS 25 - -/* SRATE here is 44100, others are derived accordingly */ -/* -#define SRATE (MY_FLOAT) 44100.0 -#define SRATE_OVER_TWO (MY_FLOAT) 22050.0 -#define ONE_OVER_SRATE (MY_FLOAT) 0.00002267573696 -#define RATE_NORM (MY_FLOAT) 0.5 -#define TWO_PI_OVER_SRATE (MY_FLOAT) 0.0001424758573 -*/ -/* SRATE here is 22050, others are derived accordingly */ - -#define SRATE (MY_FLOAT) 22050.0 -#define SRATE_OVER_TWO (MY_FLOAT) 11025.0 -#define ONE_OVER_SRATE (MY_FLOAT) 0.00004535147392 -#define RATE_NORM (MY_FLOAT) 1.0 -#define TWO_PI_OVER_SRATE (MY_FLOAT) 0.0002849517146 - -/* SRATE here is 16000, others are derived accordingly */ -/* -#define SRATE (MY_FLOAT) 16000.0 -#define SRATE_OVER_TWO (MY_FLOAT) 8000.0 -#define ONE_OVER_SRATE (MY_FLOAT) 0.0000625 -#define RATE_NORM (MY_FLOAT) 1.375 -#define TWO_PI_OVER_SRATE (MY_FLOAT) 0.000392699 -*/ -/* SRATE here is 8k, others are derived accordingly */ -/* -#define SRATE (MY_FLOAT) 8000.0 -#define SRATE_OVER_TWO (MY_FLOAT) 4000 -#define ONE_OVER_SRATE (MY_FLOAT) 0.00012500000000 -#define RATE_NORM (MY_FLOAT) 2.75625 -#define TWO_PI_OVER_SRATE (MY_FLOAT) 0.0002849517146 -*/ - -/* Yer Basic Trigonometric constants */ -#if !defined(PI) - #define PI (MY_FLOAT) 3.14159265359 -#endif -#define TWO_PI (MY_FLOAT) 6.28318530718 -#define ONE_OVER_TWO_PI (MY_FLOAT) 0.15915494309 -#define SQRT_TWO 1.414213562 - -/* States for Envelopes, etc. */ - -#define ATTACK 0 -#define DECAY 1 -#define SUSTAIN 2 -#define RELEASE 3 - -/* Machine dependent stuff, possibly useful for optimization. - * For example, changing double to float here increasesf - * performance (speed) by a whopping 4-6% on 486-flavor machines. - * BUT!! a change from float to double here increases speed by - * 30% or so on SGI machines. -*/ - -#define MY_FLOAT double -#define MY_FLOAT_SIZE 8 - -/* -#define MY_FLOAT float -#define MY_FLOAT_SIZE 4 -*/ - -/* Debugging define, causes massive printf's to come out. - * Also enables timing calculations in WaveOut class, other stuff. - */ - -/* #define _debug_ 1 */ - -/* MIDI definitions */ -#define NORM_7 (MY_FLOAT) 0.0078125 /* this is 1/128 */ - -#endif diff --git a/OnePole.cpp b/OnePole.cpp deleted file mode 100644 index 2fdad67..0000000 --- a/OnePole.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/*******************************************/ -/* One Pole Filter Class, */ -/* by Perry R. Cook, 1995-96 */ -/* The parameter gain is an additional */ -/* gain parameter applied to the filter */ -/* on top of the normalization that takes */ -/* place automatically. So the net max */ -/* gain through the system equals the */ -/* value of gain. sgain is the combina- */ -/* tion of gain and the normalization */ -/* parameter, so if you set the poleCoeff */ -/* to alpha, sgain is always set to */ -/* gain * (1.0 - fabs(alpha)). */ -/*******************************************/ - -#include "OnePole.h" - -OnePole :: OnePole() : Filter() -{ - poleCoeff = (MY_FLOAT) 0.9; - gain = (MY_FLOAT) 1.0; - sgain = (MY_FLOAT) 0.1; - outputs = (MY_FLOAT *) malloc(MY_FLOAT_SIZE); - outputs[0] = (MY_FLOAT) 0.0; -} - -OnePole :: ~OnePole() -{ - free(outputs); -} - -void OnePole :: clear() -{ - outputs[0] = (MY_FLOAT) 0.0; - lastOutput = (MY_FLOAT) 0.0; -} - -void OnePole :: setPole(MY_FLOAT aValue) -{ - poleCoeff = aValue; - if (poleCoeff > 0.0) /* Normalize gain to 1.0 max */ - sgain = gain * ((MY_FLOAT) 1.0 - poleCoeff); - else - sgain = gain * ((MY_FLOAT) 1.0 + poleCoeff); -} - -void OnePole :: setGain(MY_FLOAT aValue) -{ - gain = aValue; - if (poleCoeff > 0.0) - sgain = gain * ((MY_FLOAT) 1.0 - poleCoeff); /* Normalize gain to 1.0 max */ - else - sgain = gain * ((MY_FLOAT) 1.0 + poleCoeff); -} - -MY_FLOAT OnePole :: tick(MY_FLOAT sample) /* Perform Filter Operation */ -{ - outputs[0] = (sgain * sample) + (poleCoeff * outputs[0]); - lastOutput = outputs[0]; - return lastOutput; -} - -/************ Test Main ************************/ -/* -#include - -void main() -{ - long i; - OnePole test; - test.setPole(0.99); - for (i=0;i<150;i++) printf("%lf ",test.tick(1.0)); - printf("\n\n"); - - test.clear(); - test.setPole(0.9); - test.setGain(2.0); - for (i=0;i<150;i++) printf("%lf ",test.tick(0.5)); - printf("\n\n"); -} -*/ diff --git a/PercFlut.cpp b/PercFlut.cpp deleted file mode 100644 index 4160584..0000000 --- a/PercFlut.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************/ -/* Percussive Flute Subclass */ -/* of Algorithm 4 (TX81Z) Subclass of */ -/* 4 Operator FM Synth */ -/* by Perry R. Cook, 1995-96 */ -/******************************************/ - -#include "PercFlut.h" - -PercFlut :: PercFlut() : FM4Alg4() -{ - this->loadWaves("rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw"); - - this->setRatio(0,(MY_FLOAT) (1.50 * 1.000)); - this->setRatio(1,(MY_FLOAT) (3.00 * 0.995)); - this->setRatio(2,(MY_FLOAT) (2.99 * 1.005)); - this->setRatio(3,(MY_FLOAT) (6.00 * 0.997)); - gains[0] = __FM4Op_gains[99]; - gains[1] = __FM4Op_gains[71]; - gains[2] = __FM4Op_gains[93]; - gains[3] = __FM4Op_gains[85]; - adsr[0]->setAllTimes((MY_FLOAT) 0.05,(MY_FLOAT) 0.05, - __FM4Op_susLevels[14],(MY_FLOAT) 0.05); - adsr[1]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.50, - __FM4Op_susLevels[13],(MY_FLOAT) 0.5); - adsr[2]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.30, - __FM4Op_susLevels[11],(MY_FLOAT) 0.05); - adsr[3]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.05, - __FM4Op_susLevels[13],(MY_FLOAT) 0.01); - twozero->setGain((MY_FLOAT) 0.0); - modDepth = (MY_FLOAT) 0.005; -} - -void PercFlut :: setFreq(MY_FLOAT frequency) -{ - baseFreq = frequency; -} - -void PercFlut :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - gains[0] = amp * __FM4Op_gains[99] * 0.5; - gains[1] = amp * __FM4Op_gains[71] * 0.5; - gains[2] = amp * __FM4Op_gains[93] * 0.5; - gains[3] = amp * __FM4Op_gains[85] * 0.5; - this->setFreq(freq); - this->keyOn(); -#if defined(_debug_) - printf("PercFlut : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - diff --git a/README-Linux.txt b/README-Linux.txt index 75065a5..2ed7c15 100644 --- a/README-Linux.txt +++ b/README-Linux.txt @@ -1,42 +1,17 @@ -STK98v2: A ToolKit of Audio Synthesis Classes and Instruments in C++ +STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Version 3.0 -By Perry R. Cook, 1995-98 -With recent help by Gary P. Scavone +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99. Please read the file README.txt for more general STK information. -STK98 for Linux is currently using the OSS sound and MIDI API. The free version of OSS will probably work, though it doesn't work with as many soundcards as the commercial version (costs about $20). +STK for Linux is currently using the OSS sound and MIDI API. The free version of OSS will probably work, though it doesn't work with as many soundcards as the commercial version (which costs about $20). -STK98 should compile without much trouble, after you make the appropriate settings in Object.h and select an appropriate Makefile. Since all Linux systems should come with the GNU makefile utilities, you should be able to use either Makefile.all or Makefile.linux (which should be renamed "Makefile" before using). Typing "make" should initiate the compilation process. +STK should compile without much trouble under Linux. Since all Linux distributions typically include the GNU makefile utilities, you should be able to use the default Makefile. Typing "make" will initiate the compilation process. -NOTE REGARDING PTHREADS: The only issue which seems to crop up on different versions of Linux concerns threads. I am using the MIT pthreads API. Under RedHat Linux 4.x, I had to specifically include (the default pthread library didn't work). However, under RedHat Linux 5.0, the default works and the path doesn't exist. I've decided to assume the default works. If you get errors with regard to pthreads when you compile, you'll have to search your system for the MIT pthread distribution and change the appropriate include statements in MIDIIO.cpp, MD2SKINI.cpp, and syntmono.cpp. +NOTE REGARDING PTHREADS: -Once everything is compiled, you can use the scripts in "TCLSpecs" to run GUIs or invoke direct MIDI input controls. However, these scripts have been written for Tcl/Tk 8.0 ... they seem to work OK on older versions of Tcl/Tk under Linux, but not under IRIX. Tcl/Tk is free, so you might as well download the newest version. - -Two primary executables are created during compilation - syntmono and MD2SKINI. Syntmono is the core STK synthesis server. All distributed STK instruments are run using syntmono. MD2SKINI takes raw MIDI input, converts it to SKINI format, and outputs the result to stdout or a socket port ID (under Windoze). Control data (in the form of SKINI messages) can be fed to syntmono through three principal means - SKINI scorefiles, MD2SKINI output, and Tcl/Tk GUIs. A variety of SKINI scorefiles are distributed with STK98 and can be found in the "scores" directory. Under all platforms, a scorefile can be piped or redirected to syntmono in the following way: - - more scores/streetsf.ski | syntmono Clarinet -r -or - syntmono Clarinet -r < scores/streetsf.ski - -A number of Tcl/Tk GUIs are provided in the "TCLSpecs" directory, though you will have to install Tcl/Tk on your system to use them. Realtime SKINI control data (via MD2SKINI or GUIs) can be piped to syntmono on Unix platforms in the following way: - - MD2SKINI | syntmono Clarinet -r -i -or - wish < TCLSpecs/TCLPhys.tcl | syntmono Clarinet -r -i +The only issue which seems to crop up on different versions of Linux concerns threads. I am using the MIT pthreads API. Under RedHat Linux 4.x, I had to specifically include (the default pthread library didn't work). However, under RedHat Linux 5.0 and higher, the default works and the path doesn't exist. I've decided to assume the default works. If you get errors with regard to pthreads when you compile, you'll have to search your system for the MIT pthread distribution and change the appropriate include statements in MIDIIO.cpp, MD2SKINI.cpp, and threads.cpp. -/******************************************************/ - -Legal and Ethical: - -This software was designed and created to be made publicly available for free, primarily for academic purposes, so if you use it, pass it on with this documentation, and for free. - -If you make a million dollars with it, give me some. If you make compositions with it, put me in the program notes. - -Some of the concepts are covered by various patents, some known to me and likely others which are unknown. Many of the ones known to me are administered by the Stanford Office of Technology and Licensing. - -The good news is that large hunks of the techniques used here are public domain. To avoid subtle legal issues, I'll not state what's freely useable here, but I'll try to note within the various classes where certain things are likely to be protected by patents. - -/******************************************************/ - diff --git a/README-SGI.txt b/README-SGI.txt index 725c1ed..72bd887 100644 --- a/README-SGI.txt +++ b/README-SGI.txt @@ -1,38 +1,11 @@ -STK98v2: A ToolKit of Audio Synthesis Classes and Instruments in C++ - -By Perry R. Cook, 1995-98 -With recent help by Gary P. Scavone - -Please read the file README.txt for more general STK information. - -STK98 should compile without much trouble, after you make the appropriate settings in Object.h and select an appropriate Makefile. Apparently, SGI systems are not distributed with the GNU makefile utilities, so your safest bet is to use Makefile.sgi (which should be renamed "Makefile" before using). If you do have the GNU makefile utilities on your system, you could also use Makefile.all (which is convenient if you are on a network with a variety of different computer systems). Typing "make" should initiate the compilation process. - -Once everything is compiled, you can use the scripts in "TCLSpecs" to run GUIs or invoke direct MIDI input controls. - -Two primary executables are created during compilation - syntmono and MD2SKINI. Syntmono is the core STK synthesis server. All distributed STK instruments are run using syntmono. MD2SKINI takes raw MIDI input, converts it to SKINI format, and outputs the result to stdout or a socket port ID (under Windoze). Control data (in the form of SKINI messages) can be fed to syntmono through three principal means - SKINI scorefiles, MD2SKINI output, and Tcl/Tk GUIs. A variety of SKINI scorefiles are distributed with STK98 and can be found in the "scores" directory. Under all platforms, a scorefile can be piped or redirected to syntmono in the following way: - - more scores/streetsf.ski | syntmono Clarinet -r -or - syntmono Clarinet -r < scores/streetsf.ski - -A number of Tcl/Tk GUIs are provided in the "TCLSpecs" directory, though you will have to install Tcl/Tk on your system to use them. Realtime SKINI control data (via MD2SKINI or GUIs) can be piped to syntmono on Unix platforms in the following way: - - MD2SKINI | syntmono Clarinet -r -i -or - wish < TCLSpecs/TCLPhys.tcl | syntmono Clarinet -r -i - - -/******************************************************/ - -Legal and Ethical: - -This software was designed and created to be made publicly available for free, primarily for academic purposes, so if you use it, pass it on with this documentation, and for free. - -If you make a million dollars with it, give me some. If you make compositions with it, put me in the program notes. - -Some of the concepts are covered by various patents, some known to me and likely others which are unknown. Many of the ones known to me are administered by the Stanford Office of Technology and Licensing. - -The good news is that large hunks of the techniques used here are public domain. To avoid subtle legal issues, I'll not state what's freely useable here, but I'll try to note within the various classes where certain things are likely to be protected by patents. - -/******************************************************/ - +STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Version 3.0 + +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99. + +Please read the file README.txt for more general STK information. + +It seems that SGI systems are not distributed with the GNU Makefile utilities. The default Make utility has very limited functionality, so your safest bet is to download the GNU Makefile utilities from the Internet and use STK's default Makefile. If this is not possible, try using Makefile.sgi (make -f Makefile.sgi). + +Aside from the Makefile issues, STK should compile and run on SGI platforms without any problems. \ No newline at end of file diff --git a/README-Win.txt b/README-Win.txt index a25a873..f8cca0d 100644 --- a/README-Win.txt +++ b/README-Win.txt @@ -1,17 +1,23 @@ -STK98v2: A ToolKit of Audio Synthesis Classes and Instruments in C++ +STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Version 3.0 -By Perry R. Cook, 1995-98 -With recent help by Gary P. Scavone +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99. Please read the file README.txt for more general STK information. -Realtime sound output under Win95 is supported using either the Direct Sound (dsound.lib) API or the old WinMM (winmm.lib) API. The Direct Sound version appears to well out-perform the older API. All new versions of Win95/NT come with the Direct Sound library, but early versions did not. If you have trouble running the distributed executables (compiled for Direct Sound API), then you probably don't have Direct Sound installed on your system. You can download the necessary Direct Sound stuff from Microsoft's WWW pages (http://www.microsoft.com/directx/download.asp). If all else fails, you should be able to compile using the winmm.lib routines ... more latency, but at least it will work. +DirectX and WindowsNT Issues: +----------------------------- + +The newly offered STK realtime sound input capabilities under Windoze are only supported using the DirectSoundCapture API. The latency is pretty horrendous, but what do you expect? Also, there is a good chance you don't have DirectSoundCapture support on your computer. You should download the DirectX 6.0 (or higher) runtime libraries from Microsoft's WWW site (http://www.microsoft.com/directx/download.asp) in order to run the pre-compiled STK executables for Windoze. HOWEVER, there is no DirectSoundCapture support for WindowsNT at this time ... you'll have to wait for Windows 2000. If you wish to compile STK under WindowsNT (without realtime audio input support), you'll have to uncomment the __WINMM_API_ flag (and comment out the __WINDS_API flag) in Object.h and recompile the source code. + +Realtime sound output under Windoze is supported using either the DirectSound (dsound.lib) API or the old WinMM (winmm.lib) API. The DirectSound version appears to well out-perform the older API. All new versions of Win95/98/NT come with the DirectSound library, but early versions did not. If you have trouble running the distributed executables (compiled for DirectSound API), then you probably don't have DirectSound installed on your system. You can download the necessary DirectSound stuff from Microsoft's WWW pages (http://www.microsoft.com/directx/download.asp). If all else fails, you should be able to compile using the winmm.lib routines ... more latency, but at least it will work. Realtime MIDI input is supported using the winmm.lib API. -A Visual C++ workspace has been created for STK98, with two projects - syntmono and MD2SKINI. Everything has already been configured for you. The intermediate .obj files will be written to either the "Release" or "Debug" directories, but the executable files will be written to the main STK98 directory (where they need to be for proper execution). If you should somehow lose or hose the VC++ workspace file (STK98v2.dsw), then you will have to do a LOT of configuring to recreate it ... it's probably easier just to download the distribution again from our WWW sites. Anyway, for your benefit and mine, here is a list of things that need to be added to the various "Project Settings": +Visual C++ workspaces have been created for the various STK projects. Everything has already been configured for you. The intermediate .obj files will be written to either the "Release" or "Debug" directories, but the executable files will be written to the main project directories (where they need to be for proper execution). If you should somehow lose or hose the VC++ workspace file (STK.dsw), then you will have to do a LOT of configuring to recreate it ... it's probably easier just to download the distribution again from our WWW sites. Anyway, for your benefit and mine, here is a list of things that need to be added to the various "Project Settings": -1. Under General: Set "Output files:" to (this will put the executable in the main STK directory. +1. Under General: Set "Output files:" to (this will put the executable in the main project directory. 2. Under C/C++ > Code Generation: Set "Use run-time library:" to Multithreaded. @@ -29,37 +35,32 @@ In order for socketing to work, it is necessary to have the TCP protocol install Finally, to use it all - + PLAY SKINI SCOREFILES IN REALTIME: syntmono Clarinet -r < scores/streetsf.ski + USE TCL/TK GUIs FOR REALTIME CONTROL: -1. Open a DOS console window and start syntmono (eg. syntmono Clarinet -r -i). +1. Open a DOS console window and start syntmono (eg. syntmono Clarinet -r -is). 2. Double click on a Tcl/Tk file in TCLSpecs (eg. TCLPhys.tcl) from the Windows Explorer to start the GUI. Select the "communications" menu item and "Socket" and make the connection. 3. Start moving the sliders to control the instrument. + USE REALTIME MIDI INPUT FOR CONTROL: -1. Open a DOS console window and start syntmono (eg. syntmono Clarinet -r -i). +1. Open a DOS console window and start syntmono (eg. syntmono Clarinet -r -is). -2. Open another DOS console window and start MD2SKINI (assumes you already have - MIDI setup correctly for your computer). +2. Open another DOS console window and start MD2SKINI in the following way: + + MD2SKINI -s + + This assumes you already have MIDI setup correctly for your computer. -/******************************************************/ - -Legal and Ethical: - -This software was designed and created to be made publicly available for free, primarily for academic purposes, so if you use it, pass it on with this documentation, and for free. - -If you make a million dollars with it, give me some. If you make compositions with it, put me in the program notes. - -Some of the concepts are covered by various patents, some known to me and likely others which are unknown. Many of the ones known to me are administered by the Stanford Office of Technology and Licensing. - -The good news is that large hunks of the techniques used here are public domain. To avoid subtle legal issues, I'll not state what's freely useable here, but I'll try to note within the various classes where certain things are likely to be protected by patents. - -/******************************************************/ +WINDOWS NT ONLY: +Realtime piping seems to work under WindowsNT in much the same way as on Unix platforms. Thus, it is possible to pipe realtime control data to syntmono under WindowsNT as well. \ No newline at end of file diff --git a/README.txt b/README.txt index 368ce5f..bfdb2c4 100644 --- a/README.txt +++ b/README.txt @@ -1,33 +1,77 @@ -STK98v2: A ToolKit of Audio Synthesis Classes and Instruments in C++ +STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Version 3.0 -By Perry R. Cook, 1995-98 -With recent help by Gary P. Scavone +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99. -Please read the Legal and Ethical notes at the bottom of this document. +Please read the Legal and Ethical notes near the bottom of this document. -STK has undergone a large number of revisions, changes, and additions since its initial release in 1996. With this version 2.0 release, it has been completely ported to Linux and Win95 (not tested on WinNT but should work), as well as SGI and NeXTStep (no real time capabilities under NeXTStep). See the individual README's (eg. README-linux) for platform specific information and system requirements. In general, you will have to specify your system type in Object.h and then use either the Makefile (Unix platforms) or the VC++ workspace file (STK98v2.dsw) to compile the code. -STK now features realtime sound output and MIDI input under SGI, Linux (OSS), and Win95/NT (Direct Sound and Winmm routines). It is also possible to generate simultaneous .snd, .wav, and .mat (Matlab) output file types, as well as SKINI scorefiles using MD2SKINI. +OVERVIEW: -Two primary executables are created during compilation - syntmono and MD2SKINI. Syntmono is the core STK synthesis server. All distributed STK instruments are run using syntmono. MD2SKINI takes raw MIDI input, converts it to SKINI format, and outputs the result to stdout or a socket port ID (under Windoze). Control data (in the form of SKINI messages) can be fed to syntmono through three principal means - SKINI scorefiles, MD2SKINI output, and Tcl/Tk GUIs. A variety of SKINI scorefiles are distributed with STK98 and can be found in the "scores" directory. Under all platforms, a scorefile can be piped or redirected to syntmono in the following way: +STK is a set of audio signal processing C++ classes and instruments for music synthesis. You can use these classes to create programs which make cool sounds using a variety of synthesis techniques. This is not a terribly novel concept, except that STK is very portable (it's mostly platform-independent C and C++ code) AND it's completely user-extensible. So, the code you write using STK actually has some chance of working in another 5-10 years. STK currently runs on SGI (Irix), Linux, NeXTStep, and Windows computer platforms. Oh, and it's free for non-commercial use. The only parts of STK that are platform-dependent concern real-time sound and MIDI input and output ... but we've taken care of that for you. The interface for MIDI input and the simple Tcl/Tk graphical user interfaces (GUIs) provided is the same, so it's easy to voice and experiment in real time using either the GUIs or MIDI. - more scores/streetsf.ski | syntmono Clarinet -r +STK isn't one particular program. Rather, STK is a set of C++ classes that you can use to create your own programs. We've provided a few example applications that demonstrate some of the ways that you could use these classes. But if you have specific needs you will probably have to either modify the example programs or write a new program altogether. Further, the example programs don't have a fancy GUI wrapper. If you feel the need to have a "drag and drop" GUI, you probably don't want to use STK. Spending hundreds of hours making platform-dependent GUI code would go against one of the fundamental design goals of STK - platform independence. STK can generate simultaneous .snd, .wav, and .mat output soundfile formats (beside realtime sound output), so you can view your results using one of the numerous sound/signal analysis tools already available over the WWW (e.g. Snd, Cool Edit, Matlab). For those instances where a simple GUI with sliders and buttons is helpful, we use Tcl/Tk (which is freely distributed for all the STK supported platforms). A number of Tcl/Tk GUI scripts are distributed with the STK release. + + +SYSTEM REQUIREMENTS: + +See the individual README's (eg. README-linux) for platform specific information and system requirements. In general, you will use either the provided Makefiles (Unix platforms) or the VC++ workspace files to compile the example programs. To use the Tcl/Tk GUIs, you will need Tcl/Tk version 8.0 or higher. + + +WHAT'S NEW: + +STK has undergone several key revisions, changes, and additions since its last release in 1998. Despite being available in one form or another since 1996, we still consider STK to be alpha software. Thus, backward compatability has not been a priority. Please read the ReleaseNotes to see what has changed since the last release. + +Realtime audio input capabilities have now been added to STK, though the behavior of such is very hardware dependent. Under Linux and Irix, audio input and output are possible with very low latency. Using the Windoze DirectSound API, minimum dependable output sound latency seems to be around 15 milliseconds, while input sound latency is on the order of several hundred milliseconds! It is also possible to generate simultaneous .snd, .wav, .raw, and .mat (Matlab MAT-file) output file types, as well as SKINI scorefiles using MD2SKINI. Finally, STK should compile with non-realtime functionality on any platform with a generic C++ compiler. + +Socketing capabilities have been extended in this release to function under Unix platforms, as well as Windoze platforms. Further, the socket server thread has been updated to accept multiple simultaneous socket connections. Thus, it is now possible to have several different socket clients sending SKINI control messages to the server at the same time. Under Linux and Irix, it is also possible to pipe GUI messages through MD2SKINI, enabling both MIDI and GUI control via piping at the same time. + + +GETTING STARTED: + +A number of example executables are provided with this distribution. The effects directory contains a program that demonstrates realtime duplex mode (simultaneous audio input and output) operation, as well as several simple delay-line based effects algorithms. The MUS151 directory contains a simple two-oscillator program that can be used to demonstrate psychoacoustic masking effects. The syntmono directory offers a program for monophonic STK instrument playback and manipulation. Syntmono is the primary STK synthesis server and is used to demonstrate all the current STK instruments. MD2SKINI is an executable (currently compiles from the syntmono project) which takes raw MIDI input, converts it to SKINI format, and outputs the result to stdout or any socket host and port ID. Control data (in the form of SKINI messages) can be fed to syntmono through three principal means - SKINI scorefiles, MD2SKINI output, and Tcl/Tk GUIs. A variety of SKINI scorefiles are distributed with STK and can be found in the "scores" directory of the syntmono project. + +Unless you downloaded the distribution with precompiled Windoze binaries, it is necessary to first compile the sources. Under Linux or Irix, simply typing "make" in any of the particular project directories will begin the compilation process. If your Unix system does not have the GNU Makefile utilities, you will have to use one of the platform specific Makefiles (eg. make -f Makefile.sgi). To compile the projects under Windoze, you should use the VC++ project files provided with the STK distribution. + + +SYNTMONO: + +Syntmono is the primary STK synthesis server and is used to demonstrate all the current STK instruments. Syntmono can take realtime control (SKINI) input via pipes or sockets, or it can be fed SKINI scorefile (non-realtime) input. Syntmono can output data in realtime, .wav, .snd, .mat (Matlab MAT-file), and/or .raw formats. Assuming you have successfully compiled the syntmono executable, a scorefile can be redirected to syntmono and the output heard in realtime in the following way: + + syntmono Clarinet -r < scores/streetsf.ski + +The "-r" flag specifies the realtime output option. Typing syntmono without arguments will provide a brief description of the instruments possible and the various input/output option flags. Tcl/Tk GUIs are provided in the "tcl" directory of each project, though you will have to install Tcl/Tk version 8.0 or higher on your system to use them (older versions of Tcl/Tk under Linux seem to be more forgiving than under IRIX). Realtime SKINI control data (via MD2SKINI or GUIs) can be piped to syntmono on Unix platforms and WinNT in the following way: + + MD2SKINI | syntmono Clarinet -r -ip or - syntmono Clarinet -r < scores/streetsf.ski + wish < TCLSpecs/TCLPhys.tcl | syntmono Clarinet -r -ip -A number of Tcl/Tk GUIs are provided in the "TCLSpecs" directory, though you will have to install Tcl/Tk version 8.0 or higher on your system to use them (older versions of Tcl/Tk under Linux seem to be more forgiving than under IRIX). Realtime SKINI control data (via MD2SKINI or GUIs) can be piped to syntmono on Unix platforms and perhaps under WinNT in the following way: +The "-ip" flag specifies piped realtime input. It is not possible to use realtime pipes under Windoze95/98, so socket communication must be used instead. For socket communication, it is necessary to first start the syntmono socket server using the "-is" flag (socketed realtime input). For the time being, a default (hardwired) socket port of 2001 is being used by syntmono. After syntmono is running (and waiting for a socket client connection), either MD2SKINI or a Tcl/Tk GUI can be started. When using the GUI, it is necessary to invoke the "communications" menu item and select "socket" to establish the connection. The same procedure is also possible on Unix platforms. - MD2SKINI | syntmono Clarinet -r -i -or - wish < TCLSpecs/TCLPhys.tcl | syntmono Clarinet -r -i -It is not possible to use realtime pipes under Win95, so socket communication is used instead. Perhaps in the future, all STK communications will be conducted using sockets. For socket communication, it is necessary to first start the syntmono socket server. For the time being, a default (hardwired) socket port of 2001 is being used by syntmono. After syntmono is running (and waiting for a socket client connection), either MD2SKINI or a Tcl/Tk GUI can be started. When using the GUI, it is necessary to invoke the "communications" menu item and select "socket" to establish the connection. - -For more documentation on this ToolKit, the classes, etc, read the file HIERARCH.txt and the individual class definitions. Also check the platform specific README's for specific system requirements. +DISCLAIMER: You probably already guessed this, but just to be sure, we don't guarantee anything works. :-) It's free ... what do you expect? If you find a bug, please let us know and we'll try to correct it. You can also make suggestions, but again, no guarantees. Send email to prc@cs.princeton.edu and gary@ccrma.stanford.edu. -Perry's comments from the original distribution: + +LEGAL AND ETHICAL: + +This software was designed and created to be made publicly available for free, primarily for academic purposes, so if you use it, pass it on with this documentation, and for free. + +If you make a million dollars with it, give us some. If you make compositions with it, put us in the program notes. + +Some of the concepts are covered by various patents, some known to us and likely others which are unknown. Many of the ones known to us are administered by the Stanford Office of Technology and Licensing. + +The good news is that large hunks of the techniques used here are public domain. To avoid subtle legal issues, we'll not state what's freely useable here, but we'll try to note within the various classes where certain things are likely to be protected by patents. + + +FURTHER READING: + +For more documentation on this ToolKit, the classes, etc., read the file HIERARCH.txt and the individual class definitions. Also check the platform specific README's for specific system requirements. + + +PERRY'S NOTES FROM THE ORIGINAL DISTRIBUTION: This whole world was created with no particular hardware in mind. These examples are intended to be tutorial in nature, as a platform for the continuation of my research, and as a possible starting point for a software synthesis system. The basic motivation was to create the necessary unit generators to do the synthesis, processing, and control that I want to do and teach about. Little thought for optimization was given (see Object.cpp), and therefore improvements, especially speed enhancements, should be possible with these classes. It was written with some basic concepts in mind about how to let compilers optimize. @@ -54,8 +98,8 @@ Your question at this point might be, "But Perry, with CMix, CMusic, CSound, CSh meaningful parameter values is required to get the models to work at all. - This set of C++ unitgenerators and instruments - might help to diminish the scores of EMails I + This set of C++ unit generators and instruments + might help to diminish the scores of emails I get asking what to do with those block diagrams I put in my papers. @@ -75,17 +119,5 @@ Your question at this point might be, "But Perry, with CMix, CMusic, CSound, CSh 6) More rationalizations to follow . . . -/******************************************************/ -Legal and Ethical: - -This software was designed and created to be made publicly available for free, primarily for academic purposes, so if you use it, pass it on with this documentation, and for free. - -If you make a million dollars with it, give me some. If you make compositions with it, put me in the program notes. - -Some of the concepts are covered by various patents, some known to me and likely others which are unknown. Many of the ones known to me are administered by the Stanford Office of Technology and Licensing. - -The good news is that large hunks of the techniques used here are public domain. To avoid subtle legal issues, I'll not state what's freely useable here, but I'll try to note within the various classes where certain things are likely to be protected by patents. - -/******************************************************/ diff --git a/RTSoundIO.cpp b/RTSoundIO.cpp deleted file mode 100644 index a8ca982..0000000 --- a/RTSoundIO.cpp +++ /dev/null @@ -1,492 +0,0 @@ -/******************************************/ -/* RTSoundIO.cpp */ -/* Realtime Sound I/O Object for STK, */ -/* by Gary P. Scavone, 1998. */ -/* Based in part on code by Doug */ -/* Scott (SGI), Tim Stilson (Linux), */ -/* Bill Putnam (Win Wav), and */ -/* R. Marsanyi (DirectSound). */ -/* */ -/* At the moment, this object only */ -/* handles realtime sound output, though */ -/* input code can go here when someone */ -/* decides they need it (and writes it). */ -/* */ -/* This object provides a standard API */ -/* across all platforms for STK realtime */ -/* sound output. At the moment, this */ -/* object is only used by RTWvOut. */ -/******************************************/ - -#include "RTSoundIO.h" - -#if defined(__SGI_REALTIME_) - -#include -#include - -RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels) -{ - ALconfig audio_port_config; - int lookaheadbuffers = 8; // number of lookahead buffers - long pvbuf[2]; - int nbuf, totalBufSize; - - audio_port_config = ALnewconfig(); - if(ALsetchannels(audio_port_config, channels) < 0) { - fprintf(stderr,"Cannot initialize audio port\n"); - exit(0); - } - - /* Size the output queue */ - nbuf = (channels == 2) ? lookaheadbuffers : lookaheadbuffers/2; - totalBufSize = RT_BUFFER_SIZE * nbuf; - if(ALsetqueuesize(audio_port_config, totalBufSize) < 0) { - fprintf(stderr,"Cannot initialize audio port\n"); - exit(0); - } - - if(audio_port) ALcloseport(audio_port); - audio_port = ALopenport("sgi.driver", "w", audio_port_config); - if(!audio_port) { - fprintf(stderr,"Cannot initialize audio port\n"); - exit(0); - } - ALfreeconfig(audio_port_config); - audio_port_config = 0; - - pvbuf[0] = AL_OUTPUT_RATE; - pvbuf[1] = (long) srate; - ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2); /* set output SR */ - /* tell port to accept refill at buffers - 1 */ - ALsetfillpoint(audio_port,RT_BUFFER_SIZE * (lookaheadbuffers - 1)); -} - -RTSoundIO :: ~RTSoundIO() -{ - if(audio_port) ALcloseport(audio_port); - audio_port=0; -} - -int RTSoundIO :: playBuffer(short *buf, int bufsize) -{ - ALwritesamps(audio_port, buf, bufsize); - return 0; -} - -#elif defined(__USS_REALTIME_) - -#define ABS(x) ((x < 0) ? (-x) : (x)) - -#include -#include -#include -#include -#include - -RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels) -{ - int lookaheadbuffers = 8; // number of lookahead buffers - int nbuf, totalBufSize; - - char DEVICE_NAME[100]; - int format; - int stereo; /* 0=mono, 1=stereo */ - int stereoset; - int speed; - int BUFFER_SIZE_LOG; - int fragsize; - - BUFFER_SIZE_LOG = (int)(log10((double)RT_BUFFER_SIZE)/log10(2.0)); - - if (channels > 2) - { - fprintf(stderr,"Unsupported # of output channels!\n"); - exit(0); - } - - if (channels == 2) stereo = 1; - else stereo = 0; - - strcpy(DEVICE_NAME,"/dev/dspW"); - - if ((audio_fd = open(DEVICE_NAME, O_WRONLY, 0)) == -1) - { /* Opening device failed */ - fprintf(stderr,"Cannot open audio device: %s\n",DEVICE_NAME); - exit(0); - } - - /* Size the output queue */ - nbuf = (channels == 2) ? lookaheadbuffers : lookaheadbuffers/2; - totalBufSize = RT_BUFFER_SIZE * nbuf; - - fragsize = (nbuf << 16) + BUFFER_SIZE_LOG; - if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &fragsize)) - { - close(audio_fd); - fprintf(stderr,"Error setting audio buffer size!\n"); - exit(0); - } - - format = AFMT_S16_LE; - if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1) - { /* Fatal error */ - close(audio_fd); - fprintf(stderr,"SNDCTL_DSP_SETFMT error\n"); - exit(0); - } - - if (format != AFMT_S16_LE) - { - close(audio_fd); - fprintf(stderr,"Soundcard doesn't support 16-bit signed LE format\n"); - exit(0); - } - - stereoset = stereo; - if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereoset)==-1) - { /* Fatal error */ - close(audio_fd); - fprintf(stderr,"SNDCTL_DSP_STEREO\n"); - exit(0); - } - - if (stereoset != stereo) - { - close(audio_fd); - fprintf(stderr,"The sound card did not set correct stereo mode\n"); - exit(0); - } - - speed = (int)srate; - if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) - { /* Fatal error */ - close(audio_fd); - fprintf(stderr,"SNDCTL_DSP_SPEED\n"); - exit(0); - } - - if (ABS(speed - srate)>100) - { - close(audio_fd); - fprintf(stderr,"The device doesn't support the requested speed.\n"); - exit(0); - } -} - -RTSoundIO :: ~RTSoundIO() -{ - if(audio_fd) close(audio_fd); - audio_fd=0; -} - -int RTSoundIO :: playBuffer(short *buf, int bufsize) -{ - /* The OSS write() routine takes the buffer size in bytes, thus the - multiplication by two. - */ - int len; - - if ((len = write(audio_fd, buf, 2*bufsize)) == -1) - { - fprintf(stderr,"Audio write error!\n"); - return -1; - } - return 0; -} - -#elif defined(__WINDS_REALTIME_) - -#include - -RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels) -{ - HRESULT result; - HWND hWnd; - DWORD dwDataLen; - WAVEFORMATEX wfFormat; - DSBUFFERDESC dsbdDesc, primarydsbDesc; - LPDIRECTSOUNDBUFFER m_pDSPrimeBuffer; - BYTE *pDSBuffData; - - /* Number of buffers of size RT_BUFFER_SIZE to make secondary DS buffer */ - int nBufs = 16; - - /* Initialize pointers to NULL */ - m_pDirectSound = NULL; - m_pDSBuffer = NULL; - m_pDSPrimeBuffer = NULL; - - /* Create the DS object */ - if ((result = DirectSoundCreate(NULL, &m_pDirectSound, NULL)) != DS_OK) - { - fprintf(stderr,"Cannot open default sound device!!\n"); - exit(0); - } - hWnd = GetForegroundWindow(); - if ((result = m_pDirectSound->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE)) != DS_OK) - { - fprintf(stderr,"DS Constructor: couldn't set cooperative level!\n"); - exit(0); - } - - /* Define the wave format structure */ - wfFormat.wFormatTag = WAVE_FORMAT_PCM; - wfFormat.nChannels = channels; - wfFormat.nSamplesPerSec = (unsigned long) srate; - wfFormat.wBitsPerSample = 8 * sizeof(short); - wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8; - wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; - wfFormat.cbSize = 0; - - /* Setup the primary DS buffer description */ - ZeroMemory(&primarydsbDesc, sizeof(DSBUFFERDESC)); - primarydsbDesc.dwSize = sizeof(DSBUFFERDESC); - primarydsbDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; - primarydsbDesc.dwBufferBytes = 0; - primarydsbDesc.lpwfxFormat = NULL; - - /* Create the primary DS buffer */ - if ((result = m_pDirectSound->CreateSoundBuffer(&primarydsbDesc, - &m_pDSPrimeBuffer, NULL)) != DS_OK) - { - fprintf(stderr,"Cannot get the primary DS buffer address!\n"); - exit(0); - } - - /* Set the primary DS buffer sound format. We have to do this because - the default primary buffer is 8-bit, 22kHz! */ - if ((result = m_pDSPrimeBuffer->SetFormat(&wfFormat)) != DS_OK) - { - fprintf(stderr,"Cannot set the primary DS buffer to proper sound format!\n"); - exit(0); - } - - /* Setup the secondary DS buffer description */ - m_cbBufSize = RT_BUFFER_SIZE * sizeof(short) * nBufs; - ZeroMemory(&dsbdDesc, sizeof(DSBUFFERDESC)); - dsbdDesc.dwSize = sizeof(DSBUFFERDESC); - dsbdDesc.dwFlags = DSBCAPS_GLOBALFOCUS; - dsbdDesc.dwBufferBytes = m_cbBufSize; - dsbdDesc.lpwfxFormat = &wfFormat; - - /* Create the secondary DS buffer */ - if ((result = m_pDirectSound->CreateSoundBuffer(&dsbdDesc, - &m_pDSBuffer, NULL)) != DS_OK) - { - fprintf(stderr,"DS Constructor: couldn't create sound buffer!\n"); - exit(0); - } - - /* Lock the DS buffer */ - if ((result = m_pDSBuffer->Lock(0, m_cbBufSize, (LPLPVOID)&pDSBuffData, - &dwDataLen, NULL, NULL, 0)) != DS_OK) - { - fprintf(stderr,"DS Constructor: couldn't lock sound buffer!\n"); - exit(0); - } - - /* Zero the DS buffer */ - ZeroMemory(pDSBuffData, dwDataLen); - - /* Unlock the DS buffer */ - if ((result = m_pDSBuffer->Unlock(pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) - { - fprintf(stderr,"DS Constructor: couldn't unlock sound buffer!\n"); - exit(0); - } - - m_cbBufOffset = 0; // reset last write position to start of buffer - - /* Start the buffer playback */ - if ((result = m_pDSBuffer->Play( 0, 0, DSBPLAY_LOOPING ) != DS_OK)) - { - fprintf(stderr,"DS Constructor: couldn't play sound buffer!\n"); - exit(0); - } -} - -RTSoundIO :: ~RTSoundIO() -{ - // Cleanup the sound buffer - if (m_pDSBuffer) - { - m_pDSBuffer->Stop(); - m_pDSBuffer->Release(); - m_pDSBuffer = NULL; - } - - // Cleanup the DS object - if (m_pDirectSound) - { - m_pDirectSound->Release(); - m_pDirectSound = NULL; - } -} - -int RTSoundIO :: playBuffer(short *buf, int bufsize) -{ - HRESULT hr; - DWORD status; - LPVOID lpbuf1 = NULL; - LPVOID lpbuf2 = NULL; - DWORD dwsize1 = 0; - DWORD dwsize2 = 0; - DWORD playPos, safePos, endWrite; - DWORD millis; - - // Should be playing, right? - hr = m_pDSBuffer->GetStatus( &status ); - if (!(status && DSBSTATUS_PLAYING)) - { - printf("Buffer not playing!\n"); - } - - // Sleep until we have enough room in buffer. - hr = m_pDSBuffer->GetCurrentPosition( &playPos, &safePos ); - if( hr != DS_OK ) return -1; - if( playPos < m_cbBufOffset ) playPos += m_cbBufSize; // unwrap offset - - endWrite = m_cbBufOffset + RT_BUFFER_SIZE * sizeof(short); - while ( playPos < endWrite ) { - // Calculate number of milliseconds until we will have room, as - // time = distance * (milliseconds/second) / ((bytes/sample) * (samples/second)), - // rounded up. - millis = (DWORD) (1.0 + ((endWrite - playPos) * 1000.0) / ( sizeof(short) * SRATE)); - - // Sleep for that long - Sleep( millis ); - - // Wake up, find out where we are now - hr = m_pDSBuffer->GetCurrentPosition( &playPos, &safePos ); - if( hr != DS_OK ) return -1; - if( playPos < m_cbBufOffset ) playPos += m_cbBufSize; // unwrap offset - } - - // Lock free space in the DS - hr = m_pDSBuffer->Lock (m_cbBufOffset, RT_BUFFER_SIZE * sizeof(short), &lpbuf1, &dwsize1, &lpbuf2, &dwsize2, 0); - if (hr == DS_OK) - { - // Copy the buffer into the DS - CopyMemory(lpbuf1, buf, dwsize1); - if(NULL != lpbuf2) CopyMemory(lpbuf2, buf+dwsize1, dwsize2); - - // Update our buffer offset and unlock sound buffer - m_cbBufOffset = (m_cbBufOffset + dwsize1 + dwsize2) % m_cbBufSize; - m_pDSBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2); - } - return 0; -} - -#elif defined(__WINMM_REALTIME_) - -#include - -#define FRAMETIME (long) (1000.0 * RT_BUFFER_SIZE / SRATE) - -RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels) -{ - MMRESULT result; - WAVEFORMATEX wfx; - int bufferSize = RT_BUFFER_SIZE; - - audioPort = NULL; - - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = channels; - wfx.nSamplesPerSec = (unsigned long) srate; - wfx.nBlockAlign = sizeof(short) * channels; - wfx.nAvgBytesPerSec = (unsigned long) srate * wfx.nBlockAlign; - wfx.wBitsPerSample = 8 * sizeof(short); - wfx.cbSize = 0; - - /* Open a Wave Out device using the wave mapper to guide us */ - result = waveOutOpen(&audioPort,WAVE_MAPPER,&wfx,(DWORD)NULL,(DWORD)NULL,CALLBACK_NULL); - if (result != MMSYSERR_NOERROR) { - fprintf(stderr,"Cannot open audio port!\n"); - exit(0); - } - - for( outBufNum = 0; outBufNum < (UINT)NUM_OUT_BUFFERS; outBufNum++ ) - { - /* set up a couple of wave headers */ - whOut[outBufNum].lpData = (LPSTR)calloc(channels*bufferSize, sizeof(short)); - if (whOut[outBufNum].lpData == NULL){ - waveOutClose( audioPort ); - fprintf(stderr,"Error initializing audio buffers!\n"); - exit(0); - } - whOut[outBufNum].dwBufferLength = channels*bufferSize*sizeof(short); - whOut[outBufNum].dwBytesRecorded = 0; - whOut[outBufNum].dwUser = 1; - //whOut[outBufNum].dwFlags = 0; - whOut[outBufNum].dwFlags = WHDR_DONE; - whOut[outBufNum].dwLoops = 0; - whOut[outBufNum].lpNext = NULL; - whOut[outBufNum].reserved = 0; - } - - /* Write the first buffer out to get things going */ - outBufNum = 0; - result = waveOutPrepareHeader(audioPort, &whOut[outBufNum],sizeof(WAVEHDR)); - result = waveOutWrite(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); - - /* Keep track of time so that we know how long we can sleep */ - lastWriteTime = timeGetTime(); -} - -RTSoundIO :: ~RTSoundIO() -{ - MMRESULT result; - long timeToGo; - - /* Close Audio Port */ - if (audioPort != NULL) { - result = waveOutReset(audioPort); - for( outBufNum = 0; outBufNum < (UINT)NUM_OUT_BUFFERS; outBufNum++ ) - { - /* Loop until the next waveheader indicates that we are done */ - while( !(whOut[outBufNum].dwFlags & WHDR_DONE) ) - { - //printf("."); - timeToGo = (long) (FRAMETIME - (timeGetTime()-lastWriteTime)); - if( timeToGo > 0 ) Sleep( (long) timeToGo ); - } - /* Unprepare the header */ - result = waveOutUnprepareHeader(audioPort, &whOut[outBufNum],sizeof(WAVEHDR)); - if (whOut[outBufNum].lpData != NULL) { - free(whOut[outBufNum].lpData); - whOut[outBufNum].lpData = NULL; - } - } - result = waveOutClose(audioPort); - } -} - -int RTSoundIO :: playBuffer(short *buf, int bufsize) -{ - MMRESULT result; - long timeToGo; - - outBufNum++; - if( outBufNum >= (UINT)NUM_OUT_BUFFERS ) outBufNum = 0; - - /* Loop until the next waveheader indicates that we are done */ - while( !(whOut[outBufNum].dwFlags & WHDR_DONE) ) - { - //printf("."); - timeToGo = (long) (FRAMETIME - (timeGetTime()-lastWriteTime)); - //timeToGo = (long) (FRAMETIME * 0.5); - if( timeToGo > 0 ) Sleep( (long) timeToGo ); - } - result = waveOutUnprepareHeader(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); - - memcpy( whOut[outBufNum].lpData, buf, bufsize*sizeof(short)); - result = waveOutPrepareHeader(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); - result = waveOutWrite(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); - lastWriteTime = timeGetTime(); - - return 0; -} - -#endif diff --git a/RTSoundIO.h b/RTSoundIO.h deleted file mode 100644 index 5b1a466..0000000 --- a/RTSoundIO.h +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************/ -/* RTSoundIO.h */ -/* Realtime Sound I/O Object for STK, */ -/* by Gary P. Scavone, 1998. */ -/* Based in part on code by Doug */ -/* Scott (SGI), Tim Stilson (Linux), */ -/* Bill Putnam (Win Wav), and */ -/* R. Marsanyi (DirectSound). */ -/* */ -/* At the moment, this object only */ -/* handles realtime sound output, though */ -/* input code can go here when someone */ -/* decides they need it (and writes it). */ -/* */ -/* This object provides a standard API */ -/* across all platforms for STK realtime */ -/* sound output. At the moment, this */ -/* object is only used by RTWvOut. */ -/******************************************/ - -#if !defined(__RTSOUNDIO_h) -#define __RTSOUNDIO_h - -#include "Object.h" - -#if defined(__SGI_REALTIME_) - #include -#elif defined(__WINDS_REALTIME_) - #include - #include "include/dsound.h" -#elif defined(__WINMM_REALTIME_) - #include - #include - #define NUM_OUT_BUFFERS 6 -#endif - -class RTSoundIO : public Object -{ - protected: -#if defined(__SGI_REALTIME_) - ALport audio_port; -#elif defined(__USS_REALTIME_) - int audio_fd; -#elif defined(__WINDS_REALTIME_) - LPDIRECTSOUND m_pDirectSound; - LPDIRECTSOUNDBUFFER m_pDSBuffer; - UINT m_cbBufOffset; - UINT m_cbBufSize; -#elif defined(__WINMM_REALTIME_) - HWAVEOUT audioPort; - WAVEHDR whOut[NUM_OUT_BUFFERS]; - UINT outBufNum; - DWORD lastWriteTime; -#endif - public: - RTSoundIO(MY_FLOAT srate, int channels); - ~RTSoundIO(); - int playBuffer(short *buf, int bufsize); -}; - -#endif diff --git a/RTWvOut.cpp b/RTWvOut.cpp deleted file mode 100644 index 8e657b5..0000000 --- a/RTWvOut.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*******************************************/ -/* Raw Wave File Output Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This version opens a mono NeXT .snd */ -/* file 16bit data at 22KHz, and */ -/* pokes buffers of samples into it. */ -/*******************************************/ - -/*******************************************/ -/* SGI Real-Time Wave File Output Class, */ -/* by Perry R. Cook, 1996 */ -/* This Object can opens the SGI soundout */ -/* device, and pokes buffers of samples */ -/* into it. The real code that does the */ -/* is from Doug Scott of SGI. */ -/*******************************************/ - -/*******************************************/ -/* USS Real-Time Wave File Output Class, */ -/* by Tim Stilson, 1996 */ -/* based on code by Perry R. Cook, 1996 */ -/* This Object opens the USS sound output */ -/* device, and pokes buffers of samples */ -/* into it. */ -/*******************************************/ - -#include "RTWvOut.h" - -RTWvOut :: RTWvOut() -{ - soundIO = new RTSoundIO(SRATE, 1); - counter = 0; -} - -RTWvOut :: ~RTWvOut() -{ - soundIO->playBuffer(data,counter); - counter = 0; - while (counterplayBuffer(data,counter); - soundIO->playBuffer(data,counter); // Are these extra writes necessary in USS? - soundIO->playBuffer(data,counter); - delete soundIO; -} - -void RTWvOut :: tick(MY_FLOAT sample) -{ - - data[counter++] = (short) (sample * 32000.0); - if (counter >= RT_BUFFER_SIZE) { - soundIO->playBuffer(data,counter); - counter = 0; - } -} diff --git a/RTWvOut.h b/RTWvOut.h deleted file mode 100644 index ab54e31..0000000 --- a/RTWvOut.h +++ /dev/null @@ -1,46 +0,0 @@ -/*******************************************/ -/* Real-Time Output Class, */ -/* by Perry R. Cook, 1995-96 */ -/* Separated into just realtime by */ -/* Tim Stilson, 1996 */ -/*******************************************/ - -/*******************************************/ -/* SGI Real-Time Wave File Output Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This Object can opens the SGI soundout */ -/* device, and pokes buffers of samples */ -/* into it. The real code that does the */ -/* is originally from Doug Scott of SGI. */ -/*******************************************/ - -/*******************************************/ -/* USS Real-Time Wave File Output Class, */ -/* by Tim Stilson, 1996 */ -/* based on code by Perry R. Cook, 1996 */ -/* This Object opens the USS sound output */ -/* device, and pokes buffers of samples */ -/* into it. */ -/*******************************************/ - - -#include "Object.h" -#include "WvOut.h" -#include "RTSoundIO.h" - -#if !defined(__RTWvOut_h) -#define __RTWvOut_h - -class RTWvOut : public WvOut -{ - protected: - RTSoundIO *soundIO; - short data[RT_BUFFER_SIZE]; - long counter; - public: - RTWvOut(); - ~RTWvOut(); - void tick(MY_FLOAT sample); -}; - -#endif // defined(__RTWvOut_h) diff --git a/RawInterp.cpp b/RawInterp.cpp deleted file mode 100644 index 4320f56..0000000 --- a/RawInterp.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/*******************************************/ -/* Interpolating RawWave Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This object can open a raw 16bit data */ -/* (signed integers) file, and play back */ -/* the data once or looping, with linear */ -/* interpolation on playback. */ -/* */ -/* Made inherited from RawWave */ -/* by Gary P. Scavone (11/11/98) */ -/*******************************************/ - -#include "RawInterp.h" - -#ifdef __LITTLE_ENDIAN__ - #include "swapstuf.h" -#endif - -RawInterp :: RawInterp(char *fileName) -{ - long i; - short temp; - FILE *fd; - fd = fopen(fileName,"rb"); - if (!fd) { - printf("Couldn't find soundfile %s !!!!!!!!\n",fileName); - exit(0); - } - i = 0; - while (fread(&temp,2,1,fd)) i++; - - length = i; - fseek(fd,0,0); - data = (MY_FLOAT *) malloc(MY_FLOAT_SIZE * (length + 1)); - myData = 1; - i = 0; - while (fread(&temp,2,1,fd)) { -#ifdef __LITTLE_ENDIAN__ - temp = SwapShort (temp); -#endif - data[i] = temp; - i++; - } - data[length] = data[length-1]; - fclose(fd); - looping = 0; - time = (MY_FLOAT) 0.0; - phaseOffset = (MY_FLOAT) 0.0; - rate = (MY_FLOAT) 1.0; - finished = 0; -} - -RawInterp :: RawInterp(MY_FLOAT *someData, long aLength) -{ - - length = aLength; - data = someData; - myData = 0; - looping = 0; - time = (MY_FLOAT) 0.0; - phaseOffset = (MY_FLOAT) 0.0; - rate = (MY_FLOAT) 1.0; -} - -RawInterp :: ~RawInterp() -{ - if (myData) { - free(data); - data = 0; - } -} - -void RawInterp :: reset() -{ - time = (MY_FLOAT) 0.0; - lastOutput = (MY_FLOAT) 0.0; - finished = 0; -} - -void RawInterp :: normalize() -{ - this->normalize((MY_FLOAT) 1.0); -} - -void RawInterp :: normalize(MY_FLOAT newPeak) -{ - long i; - MY_FLOAT max = (MY_FLOAT) 0.0; - for (i=0;i<=length;i++) - if (fabs(data[i]) > max) - max = (MY_FLOAT) fabs(data[i]); - if (max > 0.0) { - max = (MY_FLOAT) 1.0 / max; - max *= newPeak; - for (i=0;i<=length;i++) - data[i] *= max; - } -} - -void RawInterp :: setRate(MY_FLOAT aRate) -{ - rate = aRate; -} - -void RawInterp :: setFreq(MY_FLOAT aFreq) -{ - rate = length * (MY_FLOAT) ONE_OVER_SRATE * aFreq; -} - -void RawInterp :: addTime(MY_FLOAT aTime) /* Add an absolute time */ -{ /* in samples */ - time += aTime; -} - -void RawInterp :: addPhase(MY_FLOAT anAngle) /* Add a time in cycles */ -{ /* Cycles here means */ - time += length * anAngle; /* 1.0 = length */ -} - -void RawInterp :: addPhaseOffset(MY_FLOAT anAngle) -{ /* Add a phase offset */ - phaseOffset = length * anAngle; /* in cycles, where */ -} /* 1.0 = length */ - -void RawInterp :: setLooping(int aLoopStatus) -{ - time = (MY_FLOAT) 0; - looping = aLoopStatus; - if (looping) data[length] = data[0]; -} - -long RawInterp :: getLength() -{ - return length; -} - -MY_FLOAT* RawInterp :: getData() -{ - return data; -} - -MY_FLOAT RawInterp :: tick() -{ - this->informTick(); - return lastOutput; -} - -int RawInterp :: isFinished() -{ - return finished; -} - -int RawInterp :: informTick() -{ - long temp; - - MY_FLOAT temp_time, alpha; - - time += rate; /* Update current time */ - - if (looping) { - while (time >= length) /* Check for end of sound */ - time -= length; /* loop back to beginning */ - while (time < 0.0) /* Check for end of sound */ - time += length; /* loop back to beginning */ - } - else { - if (time >= length) { /* Check for end of sound */ - time = length-(MY_FLOAT) 1; /* stick at end */ - finished = 1; /* Information for one-shot use */ - } - else if (time < 0.0) /* Check for end of sound */ - time = (MY_FLOAT) 0.0; /* stick at beg */ - } - - temp_time = time; - - if (phaseOffset != 0.0) { - temp_time += phaseOffset; /* Add phase offset */ - if (looping) { - while (temp_time >= length) /* Check for end of sound */ - temp_time -= length; /* loop back to beginning */ - while (temp_time < 0.0) /* Check for end of sound */ - temp_time += length; /* loop back to beginning */ - } - else { - if (temp_time >= length) /* Check for end of sound */ - temp_time = length - (MY_FLOAT) 1; /* stick at end */ - else if (temp_time < 0.0) /* check for end of sound */ - temp_time = (MY_FLOAT) 0.0; /* stick at beg */ - } - } - - temp = (long) temp_time; /* Integer part of time address */ - alpha = temp_time - (MY_FLOAT) temp; /* fractional part of time address */ - lastOutput = data[temp]; /* Do linear interpolation */ - lastOutput = lastOutput + /* same as alpha*data[temp+1] */ - (alpha * (data[temp+1] - - lastOutput)); /* + (1-alpha)data[temp] */ - - return finished; -} - -MY_FLOAT RawInterp :: lastOut() -{ - return lastOutput; -} - -/************ Test Main Program *****************/ -/* -void main() -{ - RawInterp loopWave("rawwaves/sinewave.raw"); - RawInterp oneShot("rawwaves/mandpluk.raw"); - FILE *fd; - short data; - long i; - - loopWave.setLooping(1); - loopWave.setFreq(5500); - fd = fopen("test.raw","wb"); - for (i=0;i<4096;i++) { - data = loopWave.tick(); - fwrite(&data,2,1,fd); - } - loopWave.setFreq(2750); - for (i=0;i<4096;i++) { - data = loopWave.tick(); - fwrite(&data,2,1,fd); - } - - oneShot.setLooping(0); - for (i=0;i<8192;i++) { - data = oneShot.tick(); - fwrite(&data,2,1,fd); - } - oneShot.reset(); - oneShot.setRate(0.5); - for (i=0;i<16384;i++) { - data = oneShot.tick(); - fwrite(&data,2,1,fd); - } - fclose(fd); -} -*/ diff --git a/RawInterp.h b/RawInterp.h deleted file mode 100644 index 0860f0d..0000000 --- a/RawInterp.h +++ /dev/null @@ -1,52 +0,0 @@ -/*******************************************/ -/* Interpolating RawWave Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This object can open a raw 16bit data */ -/* (signed integers) file, and play back */ -/* the data once or looping, with linear */ -/* interpolation on playback. */ -/* */ -/* Made inherited from RawWave */ -/* by Gary P. Scavone (11/11/98) */ -/*******************************************/ - -#if !defined(__RawInterp_h) -#define __RawInterp_h - -#include "Object.h" -#include "RawWave.h" - -class RawInterp : public RawWave -{ - protected: - int looping; - int myData; - int finished; - long length; - MY_FLOAT *data; - MY_FLOAT rate; - MY_FLOAT time; - MY_FLOAT phaseOffset; - MY_FLOAT lastOutput; - public: - RawInterp(char *fileName); - RawInterp(MY_FLOAT *someData,long aLength); - ~RawInterp(); - void reset(); - void normalize(); - void normalize(MY_FLOAT newPeak); - void setRate(MY_FLOAT aRate); - void setFreq(MY_FLOAT aFreq); - void addTime(MY_FLOAT aTime); - void addPhase(MY_FLOAT anAngle); - void addPhaseOffset(MY_FLOAT anAngle); - void setLooping(int aLoopStatus); - int isFinished(); - long getLength(); - MY_FLOAT* getData(); - MY_FLOAT tick(); - int informTick(); - MY_FLOAT lastOut(); -}; - -#endif diff --git a/RawLoop.cpp b/RawLoop.cpp deleted file mode 100644 index 4d956fc..0000000 --- a/RawLoop.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/*******************************************/ -/* Raw Looped Soundfile Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This Object can open a raw 16bit data */ -/* (signed integers) file, and play back */ -/* the data, looping only, with linear */ -/* interpolation on playback. */ -/*******************************************/ - -#include "RawLoop.h" - -#include "swapstuf.h" - -RawLoop :: RawLoop(char *fileName) -{ - long i; - short temp; - FILE *fd; - fd = fopen(fileName,"rb"); - if (!fd) { - printf("Couldn't find soundfile %s !!!!!!!!\n",fileName); - exit(0); - } - i = 0; - while (fread(&temp,2,1,fd)) i++; - length = i; - fseek(fd,0,0); - data = (MY_FLOAT *) malloc(MY_FLOAT_SIZE * (length + 1)); - i = 0; - while (fread(&temp,2,1,fd)) { -#ifdef __LITTLE_ENDIAN__ - temp = SwapShort (temp); -#endif - data[i] = temp; - i++; - } - data[length] = data[0]; - fclose(fd); - time = (MY_FLOAT) 0.0; - phaseOffset = (MY_FLOAT) 0.0; - rate = (MY_FLOAT) 1.0; -} - -RawLoop :: ~RawLoop() -{ - free(data); -} - -void RawLoop :: reset() -{ - time = (MY_FLOAT) 0.0; - lastOutput = (MY_FLOAT) 0.0; -} - -void RawLoop :: normalize() -{ - this->normalize((MY_FLOAT) 1.0); -} - -void RawLoop :: normalize(MY_FLOAT newPeak) -{ - long i; - MY_FLOAT max = (MY_FLOAT) 0.0; - for (i=0;i<=length;i++) - if (fabs(data[i]) > max) - max = (MY_FLOAT) fabs((double) data[i]); - if (max > 0.0) { - max = (MY_FLOAT) 1.0 / max; - max *= newPeak; - for (i=0;i<=length;i++) - data[i] *= max; - } -} - -void RawLoop :: setRate(MY_FLOAT aRate) -{ - rate = aRate; -} - -void RawLoop :: setFreq(MY_FLOAT aFreq) -{ - rate = length * ONE_OVER_SRATE * aFreq; -} - -void RawLoop :: addTime(MY_FLOAT aTime) /* Add an absolute time */ -{ /* in samples */ - time += aTime; -} - -void RawLoop :: addPhase(MY_FLOAT anAngle) /* Add a time in cycles */ -{ /* Cycles here means */ - time += length * anAngle; /* 1.0 = length */ -} - -void RawLoop :: addPhaseOffset(MY_FLOAT anAngle) -{ /* Add a phase offset */ - phaseOffset = length * anAngle; /* in cycles, where */ -} /* 1.0 = length */ - -MY_FLOAT RawLoop :: tick() -{ - long temp; - - MY_FLOAT temp_time, alpha; - - time += rate; /* Update current time */ - - while (time >= length) /* Check for end of sound */ - time -= length; /* loop back to beginning */ - while (time < 0.0) /* Check for end of sound */ - time += length; /* loop back to beginning */ - - temp_time = time; - - if (phaseOffset != 0.0) { - temp_time += phaseOffset; /* Add phase offset */ - while (temp_time >= length) /* Check for end of sound */ - temp_time -= length; /* loop back to beginning */ - while (temp_time < 0.0) /* Check for end of sound */ - temp_time += length; /* loop back to beginning */ - } - - temp = (long) temp_time; /* Integer part of time address */ - alpha = temp_time - (MY_FLOAT) temp; /* fractional part of time address */ - lastOutput = data[temp]; /* Do linear interpolation */ - lastOutput = lastOutput + /* same as alpha*data[temp+1] */ - (alpha * (data[temp+1] - - lastOutput)); /* + (1-alpha)data[temp] */ - - return lastOutput; -} - -MY_FLOAT RawLoop :: lastOut() -{ - return lastOutput; -} - -/************ Test Main Program *****************/ -/* -void main() -{ - RawLoop loopWave("rawwaves/sinewave.raw"); - FILE *fd; - short data; - long i; - - loopWave.setFreq(5500); - fd = fopen("test.raw","wb"); - for (i=0;i<4096;i++) { - data = loopWave.tick(); - fwrite(&data,2,1,fd); - } - loopWave.setFreq(2750); - for (i=0;i<4096;i++) { - data = loopWave.tick(); - fwrite(&data,2,1,fd); - } - fclose(fd); -} -*/ diff --git a/RawLoop.h b/RawLoop.h deleted file mode 100644 index 7eb2b9b..0000000 --- a/RawLoop.h +++ /dev/null @@ -1,40 +0,0 @@ -/*******************************************/ -/* Raw Looped Soundfile Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This Object can open a raw 16bit data */ -/* (signed integers) file, and play back */ -/* the data, looping only, with linear */ -/* interpolation on playback. */ -/*******************************************/ - -#if !defined(__RawLoop_h) -#define __RawLoop_h - -#include "Object.h" -#include "RawWave.h" - -class RawLoop : public RawWave -{ - protected: - long length; - MY_FLOAT *data; - MY_FLOAT rate; - MY_FLOAT time; - MY_FLOAT phaseOffset; - MY_FLOAT lastOutput; - public: - RawLoop(char *fileName); - ~RawLoop(); - void reset(); - void normalize(); - void normalize(MY_FLOAT newPeak); - void setRate(MY_FLOAT aRate); - void setFreq(MY_FLOAT aFreq); - void addTime(MY_FLOAT aTime); - void addPhase(MY_FLOAT anAngle); - void addPhaseOffset(MY_FLOAT anAngle); - MY_FLOAT tick(); - MY_FLOAT lastOut(); -}; - -#endif diff --git a/RawShot.cpp b/RawShot.cpp deleted file mode 100644 index d16a946..0000000 --- a/RawShot.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/*******************************************/ -/* Non-Interpolating One-Shot Raw Sound- */ -/* file Class, by Perry R. Cook, 1995-96 */ -/* This Object can open a raw 16bit data */ -/* (signed integers) file, and play back */ -/* the data once, with no interpolation */ -/* on playback. Once finished, it closes */ -/* the file, the file is reopened with */ -/* the reset() method. */ -/* This is useful for small memory model, */ -/* applications, or for streaming from */ -/* disk (and generally non real-time */ -/* applications). */ -/*******************************************/ - -#include "RawShot.h" - -#ifdef __LITTLE_ENDIAN__ - #include "swapstuf.h" -#endif - -RawShot :: RawShot(char *fileName) -{ - long i; - - strcpy(fileNm,fileName); - myFile = fopen(fileNm,"rb"); - if (!myFile) { - printf("Couldn't find soundfile %s !!!!!!!!\n",fileName); - exit(0); - } - - i = 0; - while (fread(&data,2,1,myFile)) i++; - length = i; - fseek(myFile,0,0); - - time = (MY_FLOAT) 0.0; - rate = (MY_FLOAT) 1.0; - lastTime = 0; - finished = 0; - gain = (MY_FLOAT) 1.0; - lastOutput = (MY_FLOAT) 0.0; -} - -RawShot :: ~RawShot() -{ - this->finish(); -} - -void RawShot :: reset() -{ - if (finished) { - myFile = fopen(fileNm,"rb"); - } - fseek(myFile,0,0); - -#if defined(_debug_) - printf("Resetting\n"); -#endif - - time = (MY_FLOAT) 0.0; - lastTime = 0; - finished = 0; - lastOutput = (MY_FLOAT) 0.0; -} - -void RawShot :: normalize() -{ - this->normalize((MY_FLOAT) 1.0); -} - -void RawShot :: normalize(MY_FLOAT newPeak) -{ - long i; - FILE *fd; - - gain = (MY_FLOAT) 0.0; - fd = fopen(fileNm,"rb"); - for (i=0;i gain) - gain = (MY_FLOAT) fabs((double) data); - } - if (gain > 0.0) { - gain = newPeak / gain; - } - fclose(fd); -} - -void RawShot :: setRate(MY_FLOAT aRate) -{ - rate = aRate; -} - -void RawShot :: finish() -{ - finished = 1; - lastOutput = (MY_FLOAT) 0.0; - if (myFile) { - fclose(myFile); - myFile = 0; - } -} - -int RawShot :: isFinished() -{ - return finished; -} - -MY_FLOAT RawShot :: tick() -{ - this->informTick(); - return lastOutput; -} - -int RawShot :: informTick() -{ - long temp; - - if (!finished) { - time += rate; /* Update current time */ - - if (time >= length) { /* Check for end of sound */ - time = (MY_FLOAT) length - 1; /* stick at end */ - finished = 1; /* Information for one-shot use */ - fclose(myFile); - myFile = 0; - } - else { - temp = (long) time; /* Integer part of time address */ - if (temp > lastTime) { /* If we cross next sample time */ - lastTime = temp; - fread(&data,2,1,myFile); /* Snarf next sample from file */ -#ifdef __LITTLE_ENDIAN__ - data = SwapShort(data); -#endif - lastOutput = data * gain; /* And save as non-interpolated data */ - } - } - } - - return finished; -} - -MY_FLOAT RawShot :: lastOut() -{ - return lastOutput; -} - -/************ Test Main Program *****************/ -/* -void main() -{ - RawShot oneShot("rawwaves/mandpluk.raw"); - FILE *fd; - short data; - long i; - - fd = fopen("test.raw","wb"); - oneShot.setRate(1.0); - while (!oneShot.informTick()) { - data = oneShot.lastOut(); - fwrite(&data,2,1,fd); - } - - oneShot.reset(); - oneShot.setRate(0.5); - for (i=0;i<16384;i++) { - data = oneShot.tick(); - fwrite(&data,2,1,fd); - } - fclose(fd); -} -*/ diff --git a/RawShot.h b/RawShot.h deleted file mode 100644 index f36d9d4..0000000 --- a/RawShot.h +++ /dev/null @@ -1,49 +0,0 @@ -/*******************************************/ -/* Non-Interpolating One-Shot Raw Sound- */ -/* file Class, by Perry R. Cook, 1995-96 */ -/* This Object can open a raw 16bit data */ -/* (signed integers) file, and play back */ -/* the data once, with no interpolation */ -/* on playback. Once finished, it closes */ -/* the file, the file is reopened with */ -/* the reset() method. */ -/* This is useful for small memory model, */ -/* applications, or for streaming from */ -/* disk (and generally non real-time */ -/* applications). */ -/*******************************************/ - -#if !defined(__RawShot_h) -#define __RawShot_h - -#include "Object.h" -#include "RawWave.h" - -class RawShot : public RawWave -{ - protected: - long length; - long lastTime; - int finished; - short data; - char fileNm[128]; - FILE *myFile; - MY_FLOAT rate; - MY_FLOAT time; - MY_FLOAT gain; - MY_FLOAT lastOutput; - public: - RawShot(char *fileName); - ~RawShot(); - void reset(); - void normalize(); - void normalize(MY_FLOAT newPeak); - void setRate(MY_FLOAT aRate); - void finish(); - int isFinished(); - MY_FLOAT tick(); - int informTick(); - MY_FLOAT lastOut(); -}; - -#endif diff --git a/RawWave.cpp b/RawWave.cpp deleted file mode 100644 index 915f141..0000000 --- a/RawWave.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/********************************************/ -/* RawWave Abstract Class, */ -/* by Gary P. Scavone, 1998 */ -/********************************************/ - -#include "RawWave.h" - -RawWave :: RawWave() -{ -} - -RawWave :: ~RawWave() -{ -} - -void RawWave :: reset() -{ -} - -void RawWave :: normalize(MY_FLOAT newPeak) -{ -} - -void RawWave :: setRate(MY_FLOAT aRate) -{ -} - -int RawWave :: informTick() -{ - return 0; -} - -MY_FLOAT RawWave :: tick() -{ - return 0.0; -} - -MY_FLOAT RawWave :: lastOut() -{ - return 0.0; -} - diff --git a/RawWave.h b/RawWave.h deleted file mode 100644 index eef2a0e..0000000 --- a/RawWave.h +++ /dev/null @@ -1,25 +0,0 @@ -/********************************************/ -/* RawWave Abstract Class, */ -/* by Gary P. Scavone, 1998 */ -/********************************************/ - -#include "Object.h" - -#if !defined(__RawWave_h) -#define __RawWave_h - -class RawWave : public Object -{ - public: - RawWave(); - virtual ~RawWave(); - virtual void reset(); - virtual void normalize(MY_FLOAT newPeak); - virtual void setRate(MY_FLOAT aRate); - virtual int informTick(); - virtual MY_FLOAT tick(); - virtual MY_FLOAT lastOut(); -}; - - -#endif // defined(__RawWave_h) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 26a7487..57d161b 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,11 +1,32 @@ STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Release 3.0 -By Perry R. Cook, 1995-98 -and Gary P. Scavone, 1997-98 +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99 -I'll call this latest release STK98v2.02. There was a v2.01 release shortly after the initial v2.0 release in July 1998, to correct a few SGI/Win return character incompatibilities. Since I am just creating this set of release notes with v2.02, I'll just gloss over the past changes. Hopefully, I'll be able to keep this document updated with future releases. - ---Gary Scavone +v3.0 +- new #define flags for OS and realtime dependencies (this will probably cause problems for most everyone, but it was necessary to make future ports easier) +- fixed Linux MIDI input bug +- fixed MIDI status masking problem in Windows +- OS type defines now in Makefile +- new RAWWAVE_PATH define in Object.h +- syntmono pulled out to separate directory and cleaned up +- socketing capabilities under Unix, as well as Windoze +- multiple simultaneous socket client connections to STK servers now possible +- MD2SKINI now can merge MIDI and piped messages under Irix and Linux (for TCL->MD2SKINI->syntmono control) +- defined INT16 and INT32 types and fixed various WvIn and WvOut classes +- updated MatWvIn and MatWvOut for new MAT-file documentation from Matlab +- new demo GUI +- minor fixes to FM behavior +- added record/duplex capabilities to RTSoundIO (Linux, SGI, and Windoze) +- fixed bugs in WavWvOut and MatWvOut header specifications +- added RawWvOut class +- new WvIn class with RawWvIn, SndWvIn, WavWvIn, MatWvIn, and RTWvIn subclasses +- removed RawWave, RawShot, RawInterp, and RawLoop classes (supplanted by RawWvIn) +- multi-channel data support in WvIn and WvOut classes using MY_MULTI data type (pointer to MY_FLOAT) and the methods mtick() and mlastOutput() +- now writing to primary buffer under Windoze when allowed by hardware +- cleaned up Object.h a bit +- pulled various utility and thread functions out of syntmono.cpp (to aid readability of the code) v2.02: diff --git a/Rhodey.cpp b/Rhodey.cpp deleted file mode 100644 index 2a8c93d..0000000 --- a/Rhodey.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************/ -/* Fender Rhodes Electric Piano Subclass */ -/* of Algorithm 5 (TX81Z) Subclass of */ -/* 4 Operator FM Synth */ -/* by Perry R. Cook, 1995-96 */ -/******************************************/ - -#include "Rhodey.h" - -Rhodey :: Rhodey() : FM4Alg5() -{ - this->loadWaves("rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/fwavblnk.raw"); - this->setRatio(0,(MY_FLOAT) 1.0); - this->setRatio(1,(MY_FLOAT) 0.5); - this->setRatio(2,(MY_FLOAT) 1.0); - this->setRatio(3,(MY_FLOAT) 15.0); - gains[0] = __FM4Op_gains[99]; - gains[1] = __FM4Op_gains[90]; - gains[2] = __FM4Op_gains[99]; - gains[3] = __FM4Op_gains[67]; - adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.00,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[3]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.25,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - twozero->setGain((MY_FLOAT) 1.0); -} - -Rhodey :: ~Rhodey() -{ - -} - -void Rhodey :: setFreq(MY_FLOAT frequency) -{ - baseFreq = frequency * (MY_FLOAT) 2.0; - waves[0]->setFreq(baseFreq * ratios[0]); - waves[1]->setFreq(baseFreq * ratios[1]); - waves[2]->setFreq(baseFreq * ratios[2]); - waves[3]->setFreq(baseFreq * ratios[3]); -} - -void Rhodey :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - gains[0] = amp * __FM4Op_gains[99]; - gains[1] = amp * __FM4Op_gains[90]; - gains[2] = amp * __FM4Op_gains[99]; - gains[3] = amp * __FM4Op_gains[67]; - this->setFreq(freq); - this->keyOn(); -#if defined(_debug_) - printf("Rhodey : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - diff --git a/SKINI11.cpp b/SKINI11.cpp deleted file mode 100644 index 8b8c137..0000000 --- a/SKINI11.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/******************************************/ -/* 2nd generation SKINI Text File Reader */ -/* Class, by Perry R. Cook, 1996 */ -/* This Object can open a SKINI File */ -/* and parse it. The file spec is mine */ -/* and mine alone, but it's all text so */ -/* that should help you figuring it out. */ -/* */ -/* SKINI (Synthesis toolKit Instrument */ -/* Network Interface) is like MIDI, but */ -/* allows for floating point control */ -/* changes, note numbers, etc. Example: */ -/* noteOn 60.01 111.132 plays a sharp */ -/* middle C with a velocity of 111.132 */ -/* See SKINI11.txt for more information */ -/* */ -/******************************************/ - -#include "SKINI11.h" - -SKINI11 :: SKINI11(char *fileName) /* Constructor for reading SKINI files */ -{ /* Use nextMessage() method */ - myFile = fopen(fileName,"r"); - if ((int) myFile < 0) printf("SKINI11: Can't open SKINI score file\n"); - this->nextMessage(); -} - -SKINI11 :: SKINI11() /* Constructor to use this object for parsing */ -{ /* SKINI Strings (coming over socket for example */ -} /* Use parseThis() method with string argument */ - -SKINI11 :: ~SKINI11() -{ -} - -/***************** SOME HANDY ROUTINES *******************/ - -#include "SKINI11.tbl" - -#define __SK_MAX_FIELDS_ 5 -#define __SK_MAX_SIZE_ 32 - -short ignore(char aChar) -{ - short ignoreIt = 0; - if (aChar == 0) ignoreIt = 1; // Null String Termination - if (aChar == '\n') ignoreIt = 1; // Carraige Return??? - if (aChar == '/') ignoreIt = 2; // Comment Line - return ignoreIt; -} - -short delimit(char aChar) -{ - if (aChar == ' ' || // Space - aChar == ',' || // Or Comma - aChar == '\t') // Or Tab - return 1; - else - return 0; -} - -short nextChar(char* aString) -{ - int i; - - for (i=0;i<__SK_MAX_SIZE_;i++) { - if ( aString[i] != ' ' && // Space - aString[i] != ',' && // Or Comma - aString[i] != '\t' ) // Or Tab - return i; - } - return 1024; -} - -int subStrings(char *aString, - char someStrings[__SK_MAX_FIELDS_][__SK_MAX_SIZE_], - int somePointrs[__SK_MAX_FIELDS_], - char *remainderString) -{ - int notDone,howMany,point,temp; - notDone = 1; - howMany = 0; - point = 0; - temp = nextChar(aString); - if (temp >= __SK_MAX_SIZE_) { - notDone = 0; - printf("Confusion here: Ignoring this line\n"); - printf("%s\n",aString); - return howMany; - } - point = temp; - somePointrs[howMany] = point; - temp = 0; - while (notDone) { - if (aString[point] == '\n') { - notDone = 0; - } - else { - someStrings[howMany][temp++] = aString[point++]; - if (temp >= __SK_MAX_SIZE_) { - howMany = 0; - return howMany; - } - if (delimit(aString[point]) || aString[point] == '\n') { - someStrings[howMany][temp] = 0; - howMany += 1; - if (howMany < __SK_MAX_FIELDS_) { - temp = nextChar(&aString[point]); - point += temp; - somePointrs[howMany-1] = point; - temp = 0; - } - else { - temp = 0; - somePointrs[howMany-1] = point; - while(aString[point] != '\n') - remainderString[temp++] = aString[point++]; - remainderString[temp] = aString[point]; - } - } - } - } -// printf("Got: %i Strings:\n",howMany); -// for (temp=0;temp 0) - which = 0; - aField = 0; - strcpy(msgTypeString,someStrings[aField]); - while ((which < __SK_MaxMsgTypes_) && - (strcmp(msgTypeString, - skini_msgs[which].messageString))) { - which += 1; - } - if (which >= __SK_MaxMsgTypes_) { - messageType = -1; - printf("Couldn't parse this message field: =%s\n %s\n", - msgTypeString,aString); - return messageType; - } - else { - messageType = skini_msgs[which].type; -// printf("Message Token = %s type = %i\n", -// msgTypeString,messageType); - } - aField += 1; - - if (someStrings[0][0] == '=') { - deltaTime = (MY_FLOAT) atof(&someStrings[aField][1]); - deltaTime = -deltaTime; - } - else { - deltaTime = (MY_FLOAT) atof(someStrings[aField]); - } -// printf("DeltaTime = %f\n",deltaTime); - aField += 1; - - channel = atoi(someStrings[aField]); -// printf("Channel = %i\n",channel); - aField += 1; - - if (skini_msgs[which].data2 != NOPE) { - if (skini_msgs[which].data2 == SK_INT) { - byteTwoInt = atoi(someStrings[aField]); - byteTwo = (MY_FLOAT) byteTwoInt; - } - else if (skini_msgs[which].data2 == SK_DBL) { - byteTwo = (MY_FLOAT) atof(someStrings[aField]); - byteTwoInt = (long) byteTwo; - } - else if (skini_msgs[which].data2 == SK_STR) { - temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */ - temp2 = 0; - while (aString[temp] != '\n') { - remainderString[temp2++] = aString[temp++]; - } - remainderString[temp2] = 0; - } - else { - byteTwoInt = skini_msgs[which].data2; - byteTwo = (MY_FLOAT) byteTwoInt; - aField -= 1; - } - - aField += 1; - if (skini_msgs[which].data3 != NOPE) { - if (skini_msgs[which].data3 == SK_INT) { - byteThreeInt = atoi(someStrings[aField]); - byteThree = (MY_FLOAT) byteThreeInt; - } - else if (skini_msgs[which].data3 == SK_DBL) { - byteThree = (MY_FLOAT) atof(someStrings[aField]); - byteThreeInt = (long) byteThree; - } - else if (skini_msgs[which].data3 == SK_STR) { - temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */ - temp2 = 0; - while (aString[temp] != '\n') { - remainderString[temp2++] = aString[temp++]; - } - remainderString[temp2] = 0; - } - else { - byteThreeInt = skini_msgs[which].data3; - byteThree = (MY_FLOAT) byteThreeInt; - } - } - else { - byteThreeInt = byteTwoInt; - byteThree = byteTwo; - } - } - } - return messageType; -} - -long SKINI11 :: nextMessage() -{ - int notDone; - char inputString[1024]; - - notDone = 1; - while (notDone) { - notDone = 0; - if (!fgets(inputString,1024,myFile)) { - printf("//End of Score. Thanks for using SKINI0.9 Bye Bye!!\n"); - messageType = -1; - return messageType; - } - else if (parseThis(inputString) == 0) { - notDone = 1; - } - } - return messageType; -} - -long SKINI11 :: getType() -{ - return messageType; -} - -long SKINI11 :: getChannel() -{ - return channel; -} - -MY_FLOAT SKINI11 :: getDelta() -{ - return deltaTime; -} - -MY_FLOAT SKINI11 :: getByteTwo() -{ - return byteTwo; -} - -long SKINI11 :: getByteTwoInt() -{ - return byteTwoInt; -} - -MY_FLOAT SKINI11 :: getByteThree() -{ - return byteThree; -} - -long SKINI11 :: getByteThreeInt() -{ - return byteThreeInt; -} - -char* SKINI11 :: getRemainderString() -{ - return remainderString; -} - -char* SKINI11 :: getMessageTypeString() -{ - return msgTypeString; -} - -char sk_tempString[1024]; - -char* SKINI11 :: whatsThisType(long type) -{ - int i = 0; - sk_tempString[0] = 0; - for (i=0;i<__SK_MaxMsgTypes_;i++) { - if (type == skini_msgs[i].type) { - strcat(sk_tempString,skini_msgs[i].messageString); - strcat(sk_tempString,","); - } - } - return sk_tempString; -} - -char* SKINI11 :: whatsThisController(long contNum) -{ - int i = 0; - sk_tempString[0] = 0; - for (i=0;i<__SK_MaxMsgTypes_;i++) { - if (skini_msgs[i].type == __SK_ControlChange_ - && contNum == skini_msgs[i].data2) { - strcat(sk_tempString,skini_msgs[i].messageString); - strcat(sk_tempString,","); - } - } - return sk_tempString; -} - -/************ Test Main Program *****************/ -/* -void main(int argc,char *argv[]) -{ - SKINI11 testFile(argv[1]); - - while(testFile.nextMessage() > 0) ; - -} -*/ - diff --git a/SKINI09.txt b/SKINI11.txt similarity index 77% rename from SKINI09.txt rename to SKINI11.txt index 762991b..7b56c73 100644 --- a/SKINI09.txt +++ b/SKINI11.txt @@ -1,8 +1,10 @@ -This describes the 0.9 implementation of SKINI: +This describes the 1.1 implementation of SKINI, +updated for the 2.x release of STK: Synthesis toolKit Instrument Network Interface -for the Synthesis Toolkit in C++ by Perry R. Cook 1996. +for the Synthesis Toolkit in C++ by Perry R. Cook +and Gary Scavone, 1999. ********************************* * Too good to be true? * @@ -10,35 +12,36 @@ for the Synthesis Toolkit in C++ by Perry R. Cook 1996. * A SKINI Haiku. * ********************************* -Profound thanks to Dan Trueman and Brad Garton for input on -this revision. Thanks also to MIDI, the NeXT MusicKit, ZIPI -and all the creators and modifiers of these for good bases +Profound thanks to Dan Trueman, Brad Garton, and +Gary Scavone for input on this revision. Thanks +also to MIDI, the NeXT MusicKit, ZIPI and all +the creators and modifiers of these for good bases upon/from which to build and depart. 1) MIDI Compatibility SKINI was designed to be MIDI compatible wherever possible, - and extend MIDI in incremental, then maybe profound ways. - + and extend MIDI in incremental, then maybe profound ways. + Differences from MIDI, and motivations, include: - Text-based messages are used, with meaningful names + Text-based messages are used, with meaningful names wherever possible. This allows any language or system - capable of formatted printing to generate SKINI. - Similarly, any system capable of reading in a string - and turning delimited fields into strings, floats, + capable of formatted printing to generate SKINI. + Similarly, any system capable of reading in a string + and turning delimited fields into strings, floats, and ints can consume SKINI for control. More importantly, humans can actually read, and even write if they want, SKINI files and streams. Use an editor and search/ - replace or macros to change a channel or control number. - Load a SKINI score into a spread sheet to apply - transformations to time, control parameters, MIDI + replace or macros to change a channel or control number. + Load a SKINI score into a spread sheet to apply + transformations to time, control parameters, MIDI velocities, etc. Put a monkey on a special typewriter and get your next great work. Life's too short to debug bit/nybble packed variable length mumble messages. Disk space gets cheaper, available bandwidth increases, music takes up so little space and bandwidth compared to video - and grapics. Live a little. + and grapics. Live a little. Floating point numbers are used wherever possible. Note Numbers, Velocities, Controller Values, and @@ -46,10 +49,10 @@ upon/from which to build and depart. scanned as ASCII double-precision floats. MIDI byte values are preserved, so that incoming MIDI bytes from an interface can be put directly into SKINI - messages. 60.0 or 60 is middle C, 127.0 or 127 is + messages. 60.0 or 60 is middle C, 127.0 or 127 is maximum velocity etc. But, unlike MIDI, 60.5 can cause a 50cent sharp middle C to be played. As with - MIDI byte values like velocity, use of the integer and + MIDI byte values like velocity, use of the integer and SKINI-added fractional parts is up to the implementor of the algorithm being controlled by SKINI messages. But the extra precision is there to be used or ignored. @@ -58,26 +61,23 @@ upon/from which to build and depart. SKINI was designed to be extensable and hackable for a number of applications: imbedded synthesis in a game or VR simulation, - scoring and mixing tasks, real-time and non-real time applications - which could benefit from a controllable sound synthesis, - JAVA controlled synthesis, or eventually maybe JAVA synthesis, - etc. SKINI is not intended to be "the mother of scorefiles," - but since the entire system is based on text representations - of names, floats, and ints, converters from one scorefile + scoring and mixing tasks, real-time and non-real time applications + which could benefit from controllable sound synthesis, + JAVA controlled synthesis, or eventually maybe JAVA synthesis, + etc. SKINI is not intended to be "the mother of scorefiles," + but since the entire system is based on text representations + of names, floats, and ints, converters from one scorefile language to SKINI, or back, should be easily created. I am basically a bottom-up designer with an awareness of top- - down design ideas, so SKINI above all reflects the needs of my + down design ideas, so SKINI above all reflects the needs of my particular research and creative projects as they have arisen and - developed. SKINI09 represents a profound advance beyond SKINI08 - (the first version), but it is likely that SKINI1.0x will + developed. SKINI11 represents a profound advance beyond SKINI08 + and SKINI09 (the first versions), future SKINI's might reflect some changes. Compatibility with prior scorefiles will be attempted, but there aren't that many scorefiles out - there yet. The one thing I will attempt to keep in mind is - enough consistency to allow the creation of a SKINI0x to SKINIx0 - file and stream converter object. SKINI09 should be fully - compatible with SKINI08. - + there yet. + 3) SKINI MESSAGES A basic SKINI message is a line of text. There are only three @@ -87,58 +87,59 @@ upon/from which to build and depart. the channel number is scanned as a long int. Channels could be socket numbers, machine IDs, serial numbers, or even unique tags for each event in a synthesis. Other fields might be used, as specified in the - SKINI09.tbl file. This is described in more detail later. - + SKINI11.tbl file. This is described in more detail later. + Fields in a SKINI line are delimited by spaces, commas, or tabs. The SKINI parser only operates on a line at a time, so a newline means the message is over. Multiple messages are - NOT allowed directly on a single line (by use of the ; for - example in C). This could be supported, but it isn't in 0.9. + NOT allowed directly on a single line (by use of the ; for + example in C). This could be supported, but it isn't in + version 1.1. - Message types include standard MIDI types like NoteOn, NoteOff, - ControlChange, etc. MIDI extension message types (messages - which look better than MIDI but actually get turned into + Message types include standard MIDI types like NoteOn, NoteOff, + ControlChange, etc. MIDI extension message types (messages + which look better than MIDI but actually get turned into MIDI-like messages) include LipTension, StringDamping, etc. NonMIDI message types include SetPath (sets a path for file - use later), and OpenReadFile (for streaming, mixing, and applying + use later), and OpenReadFile (for streaming, mixing, and applying effects to soundfiles along with synthesis, for example). Other NonMIDI message types include Trilling, HammerOn, etc. (these - translate to gestures, behaviors, and contexts for use by + translate to gestures, behaviors, and contexts for use by intellegent players and instruments using SKINI). Where possible I will still use these as MIDI extension messages, so foot switches, etc. can be used to control them in real time. - All fields other than type, time, and channel are optional, and the - types and useage of the additional fields is defined in the file - SKINI09.tbl. - - The other important file used by SKINI is SKINI09.msg, which is a + All fields other than type, time, and channel are optional, and the + types and useage of the additional fields is defined in the file + SKINI11.tbl. + + The other important file used by SKINI is SKINI11.msg, which is a set of #defines to make C code more readable, and to allow reasonably - quick re-mapping of control numbers, etc.. All of these defined - symbols are assigned integer values. For JAVA, the #defines could - be replaced by declaration and assignment statements, preserving + quick re-mapping of control numbers, etc.. All of these defined + symbols are assigned integer values. For JAVA, the #defines could + be replaced by declaration and assignment statements, preserving the look and behavior of the rest of the code. -4) C Files Used To Implement SKINI09 +4) C Files Used To Implement SKINI11 - SKINI09.cpp is an object which can either open a SKINI file, and - successively read and parse lines of text as SKINI strings, or - accept strings from another object and parse them. The latter + SKINI11.cpp is an object which can either open a SKINI file, and + successively read and parse lines of text as SKINI strings, or + accept strings from another object and parse them. The latter functionality would be used by a socket, pipe, or other connection receiving SKINI messages a line at a time, usually in real time, but not restricted to real time. - SKINI09.msg should be included by anything wanting to use the - SKINI09.cpp object. This is not mandatory, but use of the __SK_blah_ - symbols which are defined in the .msg file will help to ensure + SKINI11.msg should be included by anything wanting to use the + SKINI11.cpp object. This is not mandatory, but use of the __SK_blah_ + symbols which are defined in the .msg file will help to ensure clarity and consistency when messages are added and changed. - SKINI09.tbl is used only by the SKINI parser object (SKINI09.cpp). - In the file SKINI09.tbl, an array of structures is declared and + SKINI11.tbl is used only by the SKINI parser object (SKINI11.cpp). + In the file SKINI11.tbl, an array of structures is declared and assigned values which instruct the parser as to what the message types are, and what the fields mean for those message types. This table is compiled and linked into applications using SKINI, but - could be dynamically loaded and changed in a future version of + could be dynamically loaded and changed in a future version of SKINI. 5) SKINI Messages and the SKINI Parser: @@ -146,68 +147,68 @@ upon/from which to build and depart. The parser isn't all that smart, but neither am I. Here are the basic rules governing a valid SKINI message: - a) If the first (non-delimiter (see c)) character in a SKINI - string is '/' that line is treated as a comment and echoed + a) If the first (non-delimiter (see c)) character in a SKINI + string is '/' that line is treated as a comment and echoed to stdout. b) If there are no characters on a line, that line is treated as blank and echoed to stdout. Tabs and spaces are treated as non-characters. - c) Spaces, commas, and tabs delimit the fields in a SKINI + c) Spaces, commas, and tabs delimit the fields in a SKINI message line. (We might allow for multiple messages per - line later using the semicolon, but probably not. A series - of lines with deltaTimes of 0.0 denotes simultaneous events. - For Readability, multiple messages per line doesn't help much, + line later using the semicolon, but probably not. A series + of lines with deltaTimes of 0.0 denotes simultaneous events. + For Readability, multiple messages per line doesn't help much, so it's unlikely to be supported later). - d) The first field must be a SKINI message name. (like NoteOn). - These might become case-insensitive in SKINI09+, so don't plan + d) The first field must be a SKINI message name. (like NoteOn). + These might become case-insensitive in SKINI11+, so don't plan on exciting clever overloading of names (like noTeOn being different from NoTeON). There can be a number of leading spaces or tabs, but don't exceed 32 or so. - e) The second field must be a time specification in seconds. - For real-time applications, this field can be 0.0. A time - field can be either delta-time (most common and the only one - supported in SKINI0.8), or absolute time. Absolute time - messages have an '=' appended to the beginning of the floating - point number with no space. So 0.10000 means delta time of + e) The second field must be a time specification in seconds. + For real-time applications, this field can be 0.0. A time + field can be either delta-time (most common and the only one + supported in SKINI0.8), or absolute time. Absolute time + messages have an '=' appended to the beginning of the floating + point number with no space. So 0.10000 means delta time of 100ms., while =0.10000 means absolute time of 100 ms. Absolute - time messages make most sense in score files, but could also be - used for (loose) synchronization in a real-time context. Real - time messages should be time-ordered AND time-correct. That is, + time messages make most sense in score files, but could also be + used for (loose) synchronization in a real-time context. Real + time messages should be time-ordered AND time-correct. That is, if you've sent 100 total delta-time messages of 1.0 seconds, and then send an absolute time message of =90.0 seconds, or if you send two absolute time messages of =100.0 and =90.0 in that - order, things will get really fouled up. The SKINI0.9 parser - doesn't know about time, however. The RawWvOut device is the - master time keeper in the Synthesis Toolkit, so it should be - queried to see if absolute time messages are making sense. - There's an example of how to do that later in this document. + order, things will get really fouled up. The SKINI1.1 parser + doesn't know about time, however. The WvOut device is the + master time keeper in the Synthesis Toolkit, so it should be + queried to see if absolute time messages are making sense. + There's an example of how to do that later in this document. Absolute times are returned by the parser as negative numbers (since negative deltaTimes are not allowed). f) The third field must be an integer channel number. Don't go - crazy and think that this is just MIDI channel 0-15 (which is + crazy and think that this is just MIDI channel 0-15 (which is supported). The channel number is scanned as a long int. Channels 0-15 are in general to be treated as MIDI channels. After that - it's wide open. Channels could be socket numbers, machine IDs, + it's wide open. Channels could be socket numbers, machine IDs, serial numbers, or even unique tags for each event in a synthesis. A -1 channel can be used as don't care, omni, or other functions depending on your needs and taste. - g) All remaining fields are specified in the SKINI09.tbl file. + g) All remaining fields are specified in the SKINI11.tbl file. In general, there are maximum two more fields, which are either SK_INT (long), SK_DBL (double float), or SK_STR (string). The latter is the mechanism by which more arguments can be specified on the line, but the object using SKINI must take that string apart (retrived by using getRemainderString()) and scan it. Any excess fields are stashed in remainderString. - + 6) A Short SKINI File: - /* Howdy!!! Welcome to SKINI09, by P. Cook 1996 + /* Howdy!!! Welcome to SKINI11, by P. Cook 1999 NoteOn 0.000082 2 55 82 NoteOff 1.000000 2 55 0 @@ -239,11 +240,11 @@ upon/from which to build and depart. NoteOff 0.000000 2 71 82 NoteOff 0.000000 2 79 82 -7) The SKINI09.tbl File, How Messages are Parsed +7) The SKINI11.tbl File, How Messages are Parsed + + The SKINI11.tbl file contains an array of structures which + are accessed by the parser object SKINI11.cpp. The struct is: - The SKINI09.tbl file contains an array of structures which - are accessed by the parser object SKINI09.cpp. The struct is: - struct SKINISpec { char messageString[32]; long type; long data2; @@ -251,38 +252,38 @@ upon/from which to build and depart. }; so an assignment of one of these structs looks like: - - MessageStr$ ,type, data2, data3, - + + MessageStr$ ,type, data2, data3, + type is the message type sent back from the SKINI line parser. - data is either - NOPE : field not used, specifically, there aren't going + data is either + NOPE : field not used, specifically, there aren't going to be any more fields on this line. So if there is is NOPE in data2, data3 won't even be checked SK_INT : byte (actually scanned as 32 bit signed long int) If it's a MIDI data field which is required to - be an integer, like a controller number, it's - 0-127. Otherwise) get creative with SK_INTs + be an integer, like a controller number, it's + 0-127. Otherwise) get creative with SK_INTs SK_DBL : double precision floating point. SKINI uses these - in the MIDI context for note numbers with micro - tuning, velocities, controller values, etc. - SK_STR : only valid in final field. This allows (nearly) - arbitrary message types to be supported by simply - scanning the string to EndOfLine and then passing - it to a more intellegent handler. For example, - MIDI SYSEX (system exclusive) messages of up to - 256bytes can be read as space-delimited integers - into the 1K SK_STR buffer. Longer bulk dumps, - soundfiles, etc. should be handled as a new - message type pointing to a FileName, Socket, or - something else stored in the SK_STR field, or - as a new type of multi-line message. - - Here's a couple of lines from the SKINI09.tbl file + in the MIDI context for note numbers with micro + tuning, velocities, controller values, etc. + SK_STR : only valid in final field. This allows (nearly) + arbitrary message types to be supported by simply + scanning the string to EndOfLine and then passing + it to a more intellegent handler. For example, + MIDI SYSEX (system exclusive) messages of up to + 256bytes can be read as space-delimited integers + into the 1K SK_STR buffer. Longer bulk dumps, + soundfiles, etc. should be handled as a new + message type pointing to a FileName, Socket, or + something else stored in the SK_STR field, or + as a new type of multi-line message. + + Here's a couple of lines from the SKINI11.tbl file {"NoteOff" , __SK_NoteOff_, SK_DBL, SK_DBL}, {"NoteOn" , __SK_NoteOn_, SK_DBL, SK_DBL}, - + {"ControlChange" , __SK_ControlChange_, SK_INT, SK_DBL}, {"Volume" , __SK_ControlChange_, __SK_Volume_ , SK_DBL}, @@ -292,20 +293,20 @@ upon/from which to build and depart. The first three are basic MIDI messages. The first two would cause the parser, after recognizing a match of the string "NoteOff" or "NoteOn", to set the message type to 128 or 144 (__SK_NoteOff_ and __SK_NoteOn_ - are #defined in the file SKINI09.msg to be the MIDI byte value, without - channel, of the actual MIDI messages for NoteOn and NoteOff). The parser + are #defined in the file SKINI11.msg to be the MIDI byte value, without + channel, of the actual MIDI messages for NoteOn and NoteOff). The parser would then set the time or delta time (this is always done and is - therefore not described in the SKINI Message Struct). The next two - fields would be scanned as double-precision floats and assigned to - the byteTwo and byteThree variables of the SKINI parser. The remainder - of the line is stashed in the remainderString variable. - - The ControlChange line is basically the same as NoteOn and NoteOff, but + therefore not described in the SKINI Message Struct). The next two + fields would be scanned as double-precision floats and assigned to + the byteTwo and byteThree variables of the SKINI parser. The remainder + of the line is stashed in the remainderString variable. + + The ControlChange spec is basically the same as NoteOn and NoteOff, but the second data byte is set to an integer (for checking later as to - what MIDI control is being changed). - - The Volume line is a MIDI Extension message, which behaves like a - ControlChange message with the controller number set explicitly to + what MIDI control is being changed). + + The Volume spec is a MIDI Extension message, which behaves like a + ControlChange message with the controller number set explicitly to the value for MIDI Volume (7). Thus the following two lines would accomplish the same changing of MIDI volume on channel 2: @@ -314,28 +315,29 @@ upon/from which to build and depart. I like the 2nd line better, thus my motivation for SKINI in the first place. - - The StringDamping and StringDetune messages behave the same as + + The StringDamping and StringDetune messages behave the same as the Volume message, but use Control Numbers which aren't specifically nailed-down in MIDI. Note that these Control Numbers are carried around as long ints, so we're not limited to 0-127. If, however, - you want to use a MIDI controller to play an instrument, using + you want to use a MIDI controller to play an instrument, using controller numbers in the 0-127 range might make sense. 8) Objects using SKINI - + Here's a simple example of code which uses the SKINI object to read a SKINI file and control a single instrument. - + instrument = new Mandolin(50.0); - score = new SKINI09(argv[1]); + score = new SKINI11(argv[1]); while(score->getType() > 0) { - tempDouble = score->getDelta(); + tempDouble = score->getDelta(); if (tempDouble < 0) { tempDouble = - tempDouble; tempDouble = tempDouble - output.getTime(); if (tempDouble < 0) { - printf("Bad News Here!!! Backward Absolute Time Required.\n"); + printf("Bad News Here!!! Backward Absolute Time +Required.\n"); tempDouble = 0.0; } } @@ -367,13 +369,13 @@ upon/from which to build and depart. score->nextMessage(); } - When the score (SKINI09 object) object is created from the + When the score (SKINI11 object) object is created from the filename in argv[1], the first valid command line is read from the file and parsed. - - The score->getType() retrieves the messageType. If this is - -1, there are no more valid messages in the file and the - synthesis loop terminates. Otherwise, the message type is + + The score->getType() retrieves the messageType. If this is + -1, there are no more valid messages in the file and the + synthesis loop terminates. Otherwise, the message type is returned. getDelta() retrieves the deltaTime until the current message @@ -382,7 +384,7 @@ upon/from which to build and depart. zero, the time is interpreted as absolute time and the output device is queried as to what time it is now. That is used to form a deltaTime, and if it's positive we synthesize. If - it's negative, we print an error and pretend this never + it's negative, we print an error and pretend this never happened and we hang around hoping to eventually catch up. The rest of the code sorts out message types NoteOn, NoteOff @@ -391,7 +393,3 @@ upon/from which to build and depart. control number, but all other data is treated as double float. The last line reads and parses the next message in the file. - - - - diff --git a/ADSR.cpp b/STK/ADSR.cpp similarity index 100% rename from ADSR.cpp rename to STK/ADSR.cpp diff --git a/ADSR.h b/STK/ADSR.h similarity index 100% rename from ADSR.h rename to STK/ADSR.h diff --git a/AgogoBel.cpp b/STK/AgogoBel.cpp similarity index 77% rename from AgogoBel.cpp rename to STK/AgogoBel.cpp index 8a89ee6..0bb1129 100644 --- a/AgogoBel.cpp +++ b/STK/AgogoBel.cpp @@ -1,11 +1,11 @@ /*******************************************/ /* AgogoBell SubClass of Modal4 Instrument*/ -/* by Perry R. Cook, 1995-96 */ +/* by Perry R. Cook, 1995-96 */ /* */ -/* Controls: CONTROL1 = stickHardness */ -/* CONTROL2 = strikePosition*/ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ +/* Controls: CONTROL1 = stickHardness */ +/* CONTROL2 = strikePosition */ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ /*******************************************/ /* Modes measured from my Agogo Bell by FFT: */ @@ -16,15 +16,18 @@ AgogoBel :: AgogoBel() : Modal4() { - wave = new RawInterp("rawwaves/britestk.raw"); + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + wave = new RawWvIn(strcat(file,"rawwaves/britestk.raw"), "oneshot"); wave->normalize(); - wave->setRate((MY_FLOAT) 7.0); /* hardstick */ - this->setRatioAndReson(0, (MY_FLOAT) 1.00,(MY_FLOAT) 0.999); /* Set our */ - this->setRatioAndReson(1, (MY_FLOAT) 4.08,(MY_FLOAT) 0.999); /* resonances */ - this->setRatioAndReson(2,(MY_FLOAT) 6.669,(MY_FLOAT) 0.999); /* here */ - this->setRatioAndReson(3,(MY_FLOAT) -3725.0, (MY_FLOAT)0.999); /* (One fixed) */ - this->setFiltGain(0,(MY_FLOAT) 0.06); /* And filter */ - this->setFiltGain(1,(MY_FLOAT) 0.05); /* gains too */ + wave->setRate((MY_FLOAT) 7.0); // hardstick + this->setRatioAndReson(0, (MY_FLOAT) 1.00,(MY_FLOAT) 0.999); // Set our + this->setRatioAndReson(1, (MY_FLOAT) 4.08,(MY_FLOAT) 0.999); // resonances + this->setRatioAndReson(2,(MY_FLOAT) 6.669,(MY_FLOAT) 0.999); // here + this->setRatioAndReson(3,(MY_FLOAT) -3725.0, (MY_FLOAT)0.999); // (One fixed) + this->setFiltGain(0,(MY_FLOAT) 0.06); // And filter + this->setFiltGain(1,(MY_FLOAT) 0.05); // gains too this->setFiltGain(2,(MY_FLOAT) 0.03); this->setFiltGain(3,(MY_FLOAT) 0.02); directGain = (MY_FLOAT) 0.25; diff --git a/AgogoBel.h b/STK/AgogoBel.h similarity index 93% rename from AgogoBel.h rename to STK/AgogoBel.h index 0d56232..625cf87 100644 --- a/AgogoBel.h +++ b/STK/AgogoBel.h @@ -1,6 +1,6 @@ /*******************************************/ /* AgogoBell SubClass of Modal4 Instrument*/ -/* by Perry R. Cook, 1995-96 */ +/* by Perry R. Cook, 1995-96 */ /* */ /* Controls: CONTROL1 = stickHardness */ /* CONTROL2 = strikePosition */ diff --git a/BeeThree.cpp b/STK/BeeThree.cpp similarity index 55% rename from BeeThree.cpp rename to STK/BeeThree.cpp index 13dae5f..4a62a91 100644 --- a/BeeThree.cpp +++ b/STK/BeeThree.cpp @@ -9,23 +9,33 @@ BeeThree :: BeeThree() : FM4Alg8() { - this->loadWaves("rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw"); - this->setRatio(0,(MY_FLOAT) 0.999); - this->setRatio(1,(MY_FLOAT) 1.997); - this->setRatio(2,(MY_FLOAT) 3.006); - this->setRatio(3,(MY_FLOAT) 6.009); - gains[0] = __FM4Op_gains[95]; - gains[1] = __FM4Op_gains[95]; - gains[2] = __FM4Op_gains[99]; - gains[3] = __FM4Op_gains[95]; - adsr[0]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); - adsr[1]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); - adsr[2]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); - adsr[3]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.001,(MY_FLOAT) 0.4,(MY_FLOAT) 0.03); - twozero->setGain((MY_FLOAT) 0.1); + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file1[128]; + char file2[128]; + char file3[128]; + char file4[128]; + strcpy(file1, RAWWAVE_PATH); + strcpy(file2, RAWWAVE_PATH); + strcpy(file3, RAWWAVE_PATH); + strcpy(file4, RAWWAVE_PATH); + this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"), + strcat(file2,"rawwaves/sinewave.raw"), + strcat(file3,"rawwaves/sinewave.raw"), + strcat(file4,"rawwaves/fwavblnk.raw")); + + this->setRatio(0,(MY_FLOAT) 0.999); + this->setRatio(1,(MY_FLOAT) 1.997); + this->setRatio(2,(MY_FLOAT) 3.006); + this->setRatio(3,(MY_FLOAT) 6.009); + gains[0] = __FM4Op_gains[95]; + gains[1] = __FM4Op_gains[95]; + gains[2] = __FM4Op_gains[99]; + gains[3] = __FM4Op_gains[95]; + adsr[0]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); + adsr[1]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); + adsr[2]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); + adsr[3]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.001,(MY_FLOAT) 0.4,(MY_FLOAT) 0.03); + twozero->setGain((MY_FLOAT) 0.1); } BeeThree :: ~BeeThree() diff --git a/BeeThree.h b/STK/BeeThree.h similarity index 100% rename from BeeThree.h rename to STK/BeeThree.h diff --git a/BiQuad.cpp b/STK/BiQuad.cpp similarity index 97% rename from BiQuad.cpp rename to STK/BiQuad.cpp index a2bddab..5dc67b9 100644 --- a/BiQuad.cpp +++ b/STK/BiQuad.cpp @@ -10,7 +10,7 @@ BiQuad :: BiQuad() : Filter() { - inputs = (MY_FLOAT *) malloc(2 * MY_FLOAT_SIZE); + inputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT)); zeroCoeffs[0] = (MY_FLOAT) 0.0; zeroCoeffs[1] = (MY_FLOAT) 0.0; poleCoeffs[0] = (MY_FLOAT) 0.0; diff --git a/BiQuad.h b/STK/BiQuad.h similarity index 100% rename from BiQuad.h rename to STK/BiQuad.h diff --git a/BowTabl.cpp b/STK/BowTabl.cpp similarity index 100% rename from BowTabl.cpp rename to STK/BowTabl.cpp diff --git a/BowTabl.h b/STK/BowTabl.h similarity index 90% rename from BowTabl.h rename to STK/BowTabl.h index 28c17f1..7899b91 100644 --- a/BowTabl.h +++ b/STK/BowTabl.h @@ -3,6 +3,9 @@ /* by Perry R. Cook, 1995-96 */ /***********************************************/ +#if !defined(__BowTabl_h) +#define __BowTabl_h + #include "Object.h" class BowTabl : public Object @@ -20,3 +23,4 @@ class BowTabl : public Object MY_FLOAT lastOut(); }; +#endif diff --git a/STK/Bowed.cpp b/STK/Bowed.cpp new file mode 100644 index 0000000..d789081 --- /dev/null +++ b/STK/Bowed.cpp @@ -0,0 +1,166 @@ +/******************************************/ +/* Bowed String model ala Smith */ +/* after McIntyre, Schumacher, Woodhouse */ +/* by Perry Cook, 1995-96 */ +/* */ +/* This is a waveguide model, and thus */ +/* relates to various Stanford Univ. */ +/* and possibly Yamaha and other patents.*/ +/* */ +/* Controls: CONTROL1 = bowPressure */ +/* CONTROL2 = bowPosition */ +/* CONTROL3 = vibrFreq */ +/* MOD_WHEEL= vibrGain */ +/* */ +/******************************************/ + +#include "Bowed.h" +#include "SKINI11.msg" + +Bowed :: Bowed(MY_FLOAT lowestFreq) +{ + long length; + length = (long) (SRATE / lowestFreq + 1); + neckDelay = new DLineL(length); + length >>= 1; + bridgeDelay = new DLineL(length); + bowTabl = new BowTabl; + reflFilt = new OnePole; + bodyFilt = new BiQuad; + + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); + + adsr = new ADSR; + vibrGain = (MY_FLOAT) 0.0; + + neckDelay->setDelay((MY_FLOAT) 100.0); + bridgeDelay->setDelay((MY_FLOAT) 29.0); + + bowTabl->setSlope((MY_FLOAT) 3.0); + + reflFilt->setPole((MY_FLOAT) (0.6 - (0.1 * 22050.0 / SRATE))); + reflFilt->setGain((MY_FLOAT) 0.95); + + bodyFilt->setFreqAndReson((MY_FLOAT) 500.0, (MY_FLOAT) 0.85); + bodyFilt->setEqualGainZeroes(); + bodyFilt->setGain((MY_FLOAT) 0.2); + + vibr->normalize(); + vibr->setFreq((MY_FLOAT) 6.12723); + + adsr->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.005,(MY_FLOAT) 0.9,(MY_FLOAT) 0.01); + + betaRatio = (MY_FLOAT) 0.127236; +} + +Bowed :: ~Bowed() +{ + delete neckDelay; + delete bridgeDelay; + delete bowTabl; + delete reflFilt; + delete bodyFilt; + delete vibr; + delete adsr; +} + +void Bowed :: clear() +{ + neckDelay->clear(); + bridgeDelay->clear(); +} + +void Bowed :: setFreq(MY_FLOAT frequency) +{ + baseDelay = SRATE / frequency - (MY_FLOAT) 4.0; /* delay - approx. filter delay */ + bridgeDelay->setDelay(baseDelay * betaRatio); /* bow to bridge length */ + neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); /* bow to nut (finger) length */ +} + +void Bowed :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate) +{ + adsr->setRate(rate); + adsr->keyOn(); + maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.2 * amplitude); +} + +void Bowed :: stopBowing(MY_FLOAT rate) +{ + adsr->setRate(rate); + adsr->keyOff(); +} + +void Bowed :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + this->startBowing(amp,amp * (MY_FLOAT) 0.001); + this->setFreq(freq); +#if defined(_debug_) + printf("Bowed : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + +void Bowed :: noteOff(MY_FLOAT amp) +{ + this->stopBowing(((MY_FLOAT) 1.0 - amp) * (MY_FLOAT) 0.005); +#if defined(_debug_) + printf("Bowed : NoteOff: Amp=%lf\n",amp); +#endif +} + +void Bowed :: setVibrato(MY_FLOAT amount) +{ + vibrGain = amount; +} + +MY_FLOAT Bowed :: tick() +{ + MY_FLOAT bowVelocity; + MY_FLOAT bridgeRefl=(MY_FLOAT) 0,nutRefl=(MY_FLOAT) 0; + MY_FLOAT newVel=(MY_FLOAT) 0,velDiff=(MY_FLOAT) 0,stringVel=(MY_FLOAT) 0; + + bowVelocity = maxVelocity * adsr->tick(); + + bridgeRefl = -reflFilt->tick( + bridgeDelay->lastOut()); /* Bridge Reflection */ + nutRefl = -neckDelay->lastOut(); /* Nut Reflection */ + stringVel = bridgeRefl + nutRefl; /* Sum is String Velocity */ + velDiff = bowVelocity - stringVel; /* Differential Velocity */ + newVel = velDiff * bowTabl->lookup(velDiff); /* Non-Lin Bow Function */ + neckDelay->tick(bridgeRefl + newVel); /* Do string */ + bridgeDelay->tick(nutRefl + newVel); /* propagations */ + + if (vibrGain > 0.0) { + neckDelay->setDelay((baseDelay * ((MY_FLOAT) 1.0 - betaRatio)) + + (baseDelay * vibrGain*vibr->tick())); + } + + lastOutput = bodyFilt->tick(bridgeDelay->lastOut()); + + return lastOutput; +} + +void Bowed :: controlChange(int number, MY_FLOAT value) +{ +#if defined(_debug_) + printf("Bowed : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_BowPressure_) + bowTabl->setSlope((MY_FLOAT) 5.0 - ((MY_FLOAT) 4.0 * value * NORM_7)); + else if (number == __SK_BowPosition_) { + betaRatio = (MY_FLOAT) 0.027236 + ((MY_FLOAT) 0.2 * value * NORM_7); + bridgeDelay->setDelay(baseDelay * betaRatio); /* bow to bridge length */ + neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); /* bow to nut (finger) length */ + } + else if (number == __SK_ModFrequency_) + vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); + else if (number == __SK_ModWheel_) + vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4); + else if (number == __SK_AfterTouch_Cont_) + adsr->setTarget(value * NORM_7); + else { + printf("Bowed : Undefined Control Number!!\n"); + } +} diff --git a/STK/Bowed.h b/STK/Bowed.h new file mode 100644 index 0000000..92271f1 --- /dev/null +++ b/STK/Bowed.h @@ -0,0 +1,56 @@ +/******************************************/ +/* Bowed String model ala Smith */ +/* after McIntyre, Schumacher, Woodhouse */ +/* by Perry Cook, 1995-96 */ +/* */ +/* This is a waveguide model, and thus */ +/* relates to various Stanford Univ. */ +/* and possibly Yamaha and other patents.*/ +/* */ +/* Controls: CONTROL1 = bowPressure */ +/* CONTROL2 = bowPosition */ +/* CONTROL3 = vibrFreq */ +/* MOD_WHEEL= vibrGain */ +/* */ +/******************************************/ + +#if !defined(__Bowed_h) +#define __Bowed_h + +#include "Instrmnt.h" +#include "DLineL.h" +#include "BowTabl.h" +#include "OnePole.h" +#include "BiQuad.h" +#include "RawWvIn.h" +#include "ADSR.h" + +class Bowed : public Instrmnt +{ + protected: + DLineL *neckDelay; + DLineL *bridgeDelay; + BowTabl *bowTabl; + OnePole *reflFilt; + BiQuad *bodyFilt; + RawWvIn *vibr; + ADSR *adsr; + MY_FLOAT maxVelocity; + MY_FLOAT baseDelay; + MY_FLOAT vibrGain; + MY_FLOAT betaRatio; + public: + Bowed(MY_FLOAT lowestFreq); + ~Bowed(); + void clear(); + void startBowing(MY_FLOAT amplitude,MY_FLOAT rate); + void stopBowing(MY_FLOAT rate); + virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); + virtual void noteOff(MY_FLOAT amp); + virtual void setFreq(MY_FLOAT frequency); + void setVibrato(MY_FLOAT amount); + virtual void controlChange(int number, MY_FLOAT value); + virtual MY_FLOAT tick(); +}; + +#endif diff --git a/STK/BowedBar.cpp b/STK/BowedBar.cpp new file mode 100644 index 0000000..7a7bb57 --- /dev/null +++ b/STK/BowedBar.cpp @@ -0,0 +1,318 @@ +/*********************************************/ +/* Bowed Bar model */ +/* by Georg Essl, 1999 */ +/* For details refer to: */ +/* G.Essl, P.R.Cook: "Banded Waveguides: */ +/* Towards Physical Modelling of Bar */ +/* Percussion Instruments", ICMC'99 */ +/*********************************************/ + +#include "BowedBar.h" +#include "SKINI11.msg" +#include "Noise.h" + +/* Number of banded waveguide modes */ + +int NR_MODES=4; + +/* Contructor */ + +BowedBar :: BowedBar(MY_FLOAT lowestFreq) +{ + long i; + pluck_ = 1; + + modes[0] = (MY_FLOAT) 1.0; + modes[1] = (MY_FLOAT) 2.756; + modes[2] = (MY_FLOAT) 5.404; + modes[3] = (MY_FLOAT) 8.933; + for (i=0;i<4;i++) { + gains[i] = (MY_FLOAT) pow(0.9,(double) i); + } + + bowTabl = new BowTabl; + adsr = new ADSR; + bandpass_ = new BiQuad[NR_MODES]; + + bowTabl->setSlope((MY_FLOAT) 3.0); + adsr->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.005,(MY_FLOAT) 0.9,(MY_FLOAT) 0.01); + + freq = SRATE / 100; + length = 100; + + bowPos = 0; + lastBowPos = 0; + + for(i = 0; i 1568.0) freq = 1568.0; + + length = (int)(SRATE/freq); + + NR_MODES = 4; + for(i = 0; i 4) + delay[i].setDelay((int)(length/modes[i])); + else { + NR_MODES = i; + break; + } + /* FIX THIS BETTER!!!!! */ + delay[i].clear(); + + bandpass_[i].clear(); + Zs[i][1] = 0.0; + Zs[i][2] = 0.0; + filtOut[i] = 0.0; + filtIn[i] = 0.0; + } + + tuneBandPasses(); +} + +void BowedBar :: setStrikePosition(MY_FLOAT position) +{ + MY_FLOAT temp2; + temp2 = position * PI; + gains[0] = fabs(sin(temp2 / 2) * pow(0.9,0)); + gains[1] = fabs(sin(temp2) * pow(0.9,1)); + gains[2] = fabs(sin(temp2 * 3 / 2) * pow(0.9,2)); + gains[3] = fabs(sin(temp2 * 2) * pow(0.9,3)); +} + +void BowedBar :: tuneBandPasses() +{ + long i; + + for(i=0; isetRate(rate); + adsr->keyOn(); + maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.2 * amplitude); + maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.5 * amplitude); +} + +void BowedBar :: stopBowing(MY_FLOAT rate) +{ + adsr->setRate(rate); + adsr->keyOff(); +} + +void BowedBar :: pluck(MY_FLOAT amplitude) +{ + long i,j; + int pluckLen; + MY_FLOAT temp; + Noise noise; + + pluckLen = (int)(length/modes[NR_MODES-1]); + for (j=1;j0;j--) { + temp = amplitude*2.0*noise.tick(); + for(i=0; istartBowing(amp,amp * (MY_FLOAT) 0.001); + this->setFreq(freq); + pluck_ = 0; +#if defined(_debug_) + printf("BowedBar : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif + } + else { + for(int i=0; isetFreq(freq); + this->pluck(amp); + pluck_ = 1; + } +} + +void BowedBar :: noteOff(MY_FLOAT amp) +{ + if(!pluck_) { + this->stopBowing(((MY_FLOAT) 1.0 - amp) * (MY_FLOAT) 0.005); + } +#if defined(_debug_) + printf("BowedBar : NoteOff: Amp=%lf\n",amp); +#endif +} + +MY_FLOAT BowedBar :: tick() +{ + long k; + MY_FLOAT input; + MY_FLOAT data; + + data = 0.0; + + input = 0.0; + + if(integration_const_ == 0.0) + velinput = 0.0; + else + velinput = integration_const_ * velinput; + + for(k=0; ktick()*maxVelocity; + } + + if(pluck_) + { + input = 0.0; + } + else + { + input = bowvel - velinput; + + input = input * bowTabl->lookup(input); + + input = input/(MY_FLOAT)NR_MODES; + } + + for(k=0; ksetSlope((MY_FLOAT) 10.0 - ((MY_FLOAT) 9.0 * value * NORM_7)); + slope = (MY_FLOAT) 10.0 - ((MY_FLOAT) 9.0 * value * NORM_7); + } + else if (number == __SK_BowPosition_) + { + this->setStrikePosition(value*NORM_7); + } + else if (number == __SK_Balance_) { + bowPos = value * NORM_7; + bowTarg += 0.02 * (bowPos - lastBowPos); + lastBowPos = bowPos; + adsr->setTarget(bowPos); + } + else if (number == __SK_AfterTouch_Cont_) + { + bowPos = value * NORM_7; + bowTarg += 0.02 * (bowPos - lastBowPos); + lastBowPos = bowPos; + adsr->setTarget(bowPos); + } + else if (number == __SK_ModWheel_) + { + GAIN = 0.809 + (0.2 * (value * NORM_7)); + } + else if(number == __SK_ModFrequency_) + { + integration_const_ = value * NORM_7; + + } + else if(number == __SK_Sustain_) { + if(value < 65) + pluck_ = 1; + else + pluck_ = 0; + } + else if(number == __SK_Portamento_) { + if(value < 65) + trackVel = 0; + else + trackVel = 1; + } + else { + printf("BowedBar : Undefined Control Number!!\n"); + } +} diff --git a/STK/BowedBar.h b/STK/BowedBar.h new file mode 100644 index 0000000..7ee8417 --- /dev/null +++ b/STK/BowedBar.h @@ -0,0 +1,62 @@ +/*********************************************/ +/* Bowed Bar model */ +/* by Georg Essl, 1999 */ +/* For details refer to: */ +/* G.Essl, P.R.Cook: "Banded Waveguides: */ +/* Towards Physical Modelling of Bar */ +/* Percussion Instruments", ICMC'99 */ +/*********************************************/ + +#if !defined(__BowedBar_h) +#define __BowedBar_h + +#include "Instrmnt.h" +#include "DLineN.h" +#include "BowTabl.h" +#include "ADSR.h" +#include "BiQuad.h" + +class BowedBar : public Instrmnt +{ + protected: + BowTabl *bowTabl; + ADSR *adsr; + BiQuad *bandpass_; + + MY_FLOAT maxVelocity; + MY_FLOAT modes[4]; + DLineN delay[4]; + float Zs[4][2]; + MY_FLOAT coeffs[4][2]; + float filtOut[4]; + float filtIn[4]; + MY_FLOAT filtGain[4]; + MY_FLOAT freq; + int length; + MY_FLOAT R; + MY_FLOAT GAIN; + MY_FLOAT gains[4]; + MY_FLOAT slope; + MY_FLOAT velinput; + MY_FLOAT integration_const_; + int trackVel; + MY_FLOAT bowvel, bowTarg, bowPos, lastBowPos; + int pluck_; + + public: + BowedBar(MY_FLOAT lowestFreq=16); + ~BowedBar(); + void tuneBandPasses(); + void clear(); + void startBowing(MY_FLOAT amplitude,MY_FLOAT rate); + void stopBowing(MY_FLOAT rate); + void pluck(MY_FLOAT amp); + void setStrikePosition(MY_FLOAT position); + virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); + virtual void noteOff(MY_FLOAT amp); + virtual void setFreq(MY_FLOAT frequency); + virtual void controlChange(int number, MY_FLOAT value); + virtual MY_FLOAT tick(); +}; + +#endif diff --git a/STK/Brass.cpp b/STK/Brass.cpp new file mode 100644 index 0000000..e4b3198 --- /dev/null +++ b/STK/Brass.cpp @@ -0,0 +1,135 @@ +/******************************************/ +/* Waveguide Brass Instrument Model ala */ +/* Cook (TBone, HosePlayer) */ +/* by Perry R. Cook, 1995-96 */ +/* */ +/* This is a waveguide model, and thus */ +/* relates to various Stanford Univ. */ +/* and possibly Yamaha and other patents.*/ +/* */ +/* Controls: CONTROL1 = lipTension */ +/* CONTROL2 = slideLength */ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ +/******************************************/ + +#include "Brass.h" +#include "SKINI11.msg" + +Brass :: Brass(MY_FLOAT lowestFreq) +{ + length = (long) (SRATE / lowestFreq + 1); + delayLine = new DLineA(length); + lipFilter = new LipFilt; + dcBlock = new DCBlock; + adsr = new ADSR; + adsr->setAllTimes((MY_FLOAT) 0.005, (MY_FLOAT) 0.001, (MY_FLOAT) 1.0, (MY_FLOAT) 0.010); + + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); + this->clear(); + + vibr->normalize(); + vibr->setFreq((MY_FLOAT) 6.137); + vibrGain = (MY_FLOAT) 0.05; /* breath periodic vibrato component */ + + maxPressure = (MY_FLOAT) 0.0; +} + +Brass :: ~Brass() +{ + delete delayLine; + delete lipFilter; + delete dcBlock; + delete adsr; + delete vibr; +} + +void Brass :: clear() +{ + delayLine->clear(); + lipFilter->clear(); + dcBlock->clear(); +} + +void Brass :: setFreq(MY_FLOAT frequency) +{ + slideTarget = (SRATE / frequency * (MY_FLOAT) 2.0) + (MY_FLOAT) 3.0; + /* fudge correction for filter delays */ + delayLine->setDelay(slideTarget); /* we'll play a harmonic */ + lipTarget = frequency; + lipFilter->setFreq(frequency); +} + +void Brass :: setLip(MY_FLOAT frequency) +{ + lipFilter->setFreq(frequency); +} + +void Brass :: startBlowing(MY_FLOAT amplitude,MY_FLOAT rate) +{ + adsr->setAttackRate(rate); + maxPressure = amplitude; + adsr->keyOn(); +} + +void Brass :: stopBlowing(MY_FLOAT rate) +{ + adsr->setReleaseRate(rate); + adsr->keyOff(); +} + +void Brass :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + this->setFreq(freq); + this->startBlowing(amp, amp * (MY_FLOAT) 0.001); +#if defined(_debug_) + printf("Brass : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + +void Brass :: noteOff(MY_FLOAT amp) +{ + this->stopBlowing(amp * (MY_FLOAT) 0.005); +#if defined(_debug_) + printf("Brass : NoteOff: Amp=%lf\n",amp); +#endif +} + +MY_FLOAT Brass :: tick() +{ + MY_FLOAT breathPressure; + + breathPressure = maxPressure * adsr->tick(); + breathPressure += vibrGain * vibr->tick(); + lastOutput = delayLine->tick( /* bore delay */ + dcBlock->tick( /* block DC */ + lipFilter->tick((MY_FLOAT) 0.3 * breathPressure, /* mouth input */ + (MY_FLOAT) 0.85 * delayLine->lastOut()))); /* and bore reflection */ + return lastOutput; +} + +void Brass :: controlChange(int number, MY_FLOAT value) +{ + MY_FLOAT temp; +#if defined(_debug_) + printf("Brass : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_LipTension_) { + temp = lipTarget * (MY_FLOAT) pow(4.0,(2.0*value*NORM_7) - 1.0); + this->setLip(temp); + } + else if (number == __SK_SlideLength_) + delayLine->setDelay(slideTarget * ((MY_FLOAT) 0.5 + (value * NORM_7))); + else if (number == __SK_ModFrequency_) + vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); + else if (number == __SK_ModWheel_ ) + vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4); + else if (number == __SK_AfterTouch_Cont_) + adsr->setTarget(value * NORM_7); + else { + printf("Brass : Undefined Control Number!!\n"); + } +} diff --git a/Brass.h b/STK/Brass.h similarity index 91% rename from Brass.h rename to STK/Brass.h index c3b8635..e276ab7 100644 --- a/Brass.h +++ b/STK/Brass.h @@ -9,8 +9,8 @@ /* */ /* Controls: CONTROL1 = lipTension */ /* CONTROL2 = slideLength */ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ /******************************************/ #if !defined(__Brass_h) @@ -21,7 +21,7 @@ #include "LipFilt.h" #include "DCBlock.h" #include "ADSR.h" -#include "RawLoop.h" +#include "RawWvIn.h" class Brass: public Instrmnt { @@ -30,7 +30,7 @@ class Brass: public Instrmnt LipFilt *lipFilter; DCBlock *dcBlock; ADSR *adsr; - RawLoop *vibr; + RawWvIn *vibr; long length; MY_FLOAT lipTarget; MY_FLOAT slideTarget; diff --git a/Clarinet.cpp b/STK/Clarinet.cpp similarity index 95% rename from Clarinet.cpp rename to STK/Clarinet.cpp index 4c7baad..1133b8f 100644 --- a/Clarinet.cpp +++ b/STK/Clarinet.cpp @@ -26,7 +26,11 @@ Clarinet :: Clarinet(MY_FLOAT lowestFreq) filter = new OneZero; envelope = new Envelope; noise = new Noise; - vibr = new RawLoop("rawwaves/sinewave.raw"); + + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); vibr->normalize(); vibr->setFreq((MY_FLOAT) 5.735); outputGain = (MY_FLOAT) 1.0; diff --git a/Clarinet.h b/STK/Clarinet.h similarity index 97% rename from Clarinet.h rename to STK/Clarinet.h index fdda6b4..9a2d759 100644 --- a/Clarinet.h +++ b/STK/Clarinet.h @@ -22,7 +22,7 @@ #include "OneZero.h" #include "Envelope.h" #include "Noise.h" -#include "RawLoop.h" +#include "RawWvIn.h" class Clarinet : public Instrmnt { @@ -32,7 +32,7 @@ class Clarinet : public Instrmnt OneZero *filter; Envelope *envelope; Noise *noise; - RawLoop *vibr; + RawWvIn *vibr; long length; MY_FLOAT outputGain; MY_FLOAT noiseGain; diff --git a/DCBlock.cpp b/STK/DCBlock.cpp similarity index 58% rename from DCBlock.cpp rename to STK/DCBlock.cpp index 055d305..4a8844e 100644 --- a/DCBlock.cpp +++ b/STK/DCBlock.cpp @@ -12,30 +12,29 @@ DCBlock :: DCBlock() { - inputs = (MY_FLOAT *) malloc(MY_FLOAT_SIZE); - outputs = (MY_FLOAT *) malloc(MY_FLOAT_SIZE); - this->clear(); - + inputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT)); + outputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT)); + this->clear(); } DCBlock :: ~DCBlock() { - free(inputs); - free(outputs); + free(inputs); + free(outputs); } void DCBlock :: clear() { - outputs[0] = (MY_FLOAT) 0.0; - inputs[0] = (MY_FLOAT) 0.0; - lastOutput = (MY_FLOAT) 0.0; + outputs[0] = (MY_FLOAT) 0.0; + inputs[0] = (MY_FLOAT) 0.0; + lastOutput = (MY_FLOAT) 0.0; } MY_FLOAT DCBlock :: tick(MY_FLOAT sample) { - outputs[0] = sample - inputs[0] + ((MY_FLOAT) 0.99 * outputs[0]); - inputs[0] = sample; - lastOutput = outputs[0]; - return lastOutput; + outputs[0] = sample - inputs[0] + ((MY_FLOAT) 0.99 * outputs[0]); + inputs[0] = sample; + lastOutput = outputs[0]; + return lastOutput; } diff --git a/DCBlock.h b/STK/DCBlock.h similarity index 66% rename from DCBlock.h rename to STK/DCBlock.h index 55deb9f..e25f0a4 100644 --- a/DCBlock.h +++ b/STK/DCBlock.h @@ -1,6 +1,7 @@ /*******************************************/ -/* DC Blocking Filter */ -/* by Perry R. Cook, 1995-96 */ +/* DC Blocking Filter */ +/* by Perry R. Cook, 1995-96 */ +/* */ /* This guy is very helpful in, uh, */ /* blocking DC. Needed because a simple */ /* low-pass reflection filter allows DC */ @@ -15,11 +16,11 @@ class DCBlock : public Filter { - public: - DCBlock(); - ~DCBlock(); - void clear(); - MY_FLOAT tick(MY_FLOAT sample); + public: + DCBlock(); + ~DCBlock(); + void clear(); + MY_FLOAT tick(MY_FLOAT sample); }; #endif diff --git a/STK/DLineA.cpp b/STK/DLineA.cpp new file mode 100644 index 0000000..d6993c4 --- /dev/null +++ b/STK/DLineA.cpp @@ -0,0 +1,107 @@ +/*******************************************/ +/* AllPass Interpolating Delay Line */ +/* Object by Perry R. Cook 1995-96. */ +/* Revised by Gary P. Scavone, 1999. */ +/* */ +/* This one uses a delay line of maximum */ +/* length specified on creation, and */ +/* interpolates fractional length using */ +/* an all-pass filter. This version is */ +/* more efficient for computing static */ +/* length delay lines (alpha and coeff */ +/* are computed only when the length */ +/* is set, there probably is a more */ +/* efficient computational form if alpha */ +/* is changed often (each sample)). */ +/* */ +/*******************************************/ + +#include "DLineA.h" + +DLineA :: DLineA() +{ + long i; + // Default max delay length set to 2047. + length = 2048; + inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT)); + for (i=0;iclear(); + inPoint = 0; + outPoint = length >> 1; +} + +DLineA :: DLineA(long max_length) +{ + long i; + length = max_length; + inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT)); + for (i=0;iclear(); + inPoint = 0; + outPoint = length >> 1; +} + +DLineA :: ~DLineA() +{ + free(inputs); +} + +void DLineA :: clear() +{ + long i; + for (i=0;i length-1) { // if delay is too big, + printf("DLineA: Delay length too big.\n"); + printf("Setting to maximum length of %ld.\n",length-1); + outPointer = inPoint - 18.0; // force delay to max_length + } + else if (lag < 0.1) { + printf("DLineA: Delays < 0.1 not possible with current structure.\n"); + printf("Setting delay length to 0.1.\n"); + outPointer = inPoint + 0.8999999999; + } + else + outPointer = inPoint - lag + 1.0; // outPoint chases inpoint + + if (outPointer < 0) + outPointer += length; // modulo table length + outPoint = (long) outPointer; // Integer part of delay + alpha = 1.0 + outPoint - outPointer; // fractional part of delay + + if (alpha == 0.0) { // exact integer delay + outPoint -= 1; + if (outPoint < 0) outPoint += length; + } + + if (alpha<0.1) { // Hack to avoid pole/zero + outPoint += 1; // cancellation. Keeps allpass + if (outPoint >= length) outPoint -= length; + alpha += (MY_FLOAT) 1.0; // delay in range of .1 to 1.1 + } + + coeff = ((MY_FLOAT) 1.0 - alpha) / + ((MY_FLOAT) 1.0 + alpha); // coefficient for all pass +} + +MY_FLOAT DLineA :: tick(MY_FLOAT sample) // Take sample, yield sample +{ + MY_FLOAT temp; + inputs[inPoint++] = sample; // Write input sample + if (inPoint == length) // Increment input pointer + inPoint -= length; // modulo length + temp = inputs[outPoint++]; // filter input + if (outPoint == length) // Increment output pointer + outPoint -= length; // modulo length + lastOutput = -coeff * lastOutput; // delayed output + lastOutput += lastIn + (coeff * temp); // input + delayed Input + lastIn = temp; + return lastOutput; // save output and return +} diff --git a/DLineA.h b/STK/DLineA.h similarity index 79% rename from DLineA.h rename to STK/DLineA.h index 0238e5d..57f2a4d 100644 --- a/DLineA.h +++ b/STK/DLineA.h @@ -1,15 +1,16 @@ /*******************************************/ -/* */ /* AllPass Interpolating Delay Line */ -/* Object by Perry R. Cook 1995-96 */ +/* Object by Perry R. Cook 1995-96. */ +/* Revised by Gary P. Scavone, 1999. */ +/* */ /* This one uses a delay line of maximum */ -/* length specified on creation, and */ +/* length specified on creation, and */ /* interpolates fractional length using */ /* an all-pass filter. This version is */ /* more efficient for computing static */ /* length delay lines (alpha and coeff */ -/* are computed only when the length */ -/* is set, there probably is a more */ +/* are computed only when the length */ +/* is set, there probably is a more */ /* efficient computational form if alpha */ /* is changed often (each sample)). */ /* */ @@ -31,6 +32,7 @@ class DLineA : public Filter MY_FLOAT coeff; MY_FLOAT lastIn; public: + DLineA(); DLineA(long max_length); ~DLineA(); void clear(); diff --git a/STK/DLineL.cpp b/STK/DLineL.cpp new file mode 100644 index 0000000..2ce064c --- /dev/null +++ b/STK/DLineL.cpp @@ -0,0 +1,77 @@ +/*******************************************/ +/* Linearly Interpolating Delay Line */ +/* Object by Perry R. Cook 1995-96 */ +/* This one uses a delay line of maximum */ +/* length specified on creation, and */ +/* linearly interpolates fractional */ +/* length. It is designed to be more */ +/* efficient if the delay length is not */ +/* changed very often. */ +/*******************************************/ + +#include "DLineL.h" + +DLineL :: DLineL() +{ + // Default max delay length set to 2047. + length = 2048; + inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT)); + this->clear(); + inPoint = 0; + outPoint = length >> 1; +} + +DLineL :: DLineL(long max_length) +{ + length = max_length; + inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT)); + this->clear(); + inPoint = 0; + outPoint = length >> 1; +} + +DLineL :: ~DLineL() +{ + free(inputs); +} + +void DLineL :: clear() +{ + long i; + for (i=0;i length-1) { // if delay is too big, + printf("DLineL: Delay length too big.\n"); + printf("Setting to maximum length of %ld.\n",length-1); + outPointer = inPoint + 1; // force delay to max_length + } + else + outPointer = inPoint - lag; // read chases write + while (outPointer<0) + outPointer += length; // modulo maximum length + outPoint = (long) outPointer; // integer part + alpha = outPointer - outPoint; // fractional part + omAlpha = (MY_FLOAT) 1.0 - alpha; // 1.0 - fractional part (more efficient) +} + +MY_FLOAT DLineL :: tick(MY_FLOAT sample) // Take one, yield one +{ + inputs[inPoint++] = sample; // Input next sample + if (inPoint == length) // Check for end condition + inPoint -= length; + lastOutput = inputs[outPoint++] * omAlpha; // first 1/2 of interpolation + if (outPointclear(); + inPoint = 0; + outPoint = length >> 1; +} + +DLineN :: DLineN(long max_length) +{ + // Writing before reading allows delays from 0 to length-1. + // Thus, if we want to allow a delay of max_length, we need + // a delay-line of length = max_length+1. + length = max_length+1; + inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT)); + this->clear(); + inPoint = 0; + outPoint = length >> 1; +} + +DLineN :: ~DLineN() +{ + free(inputs); +} + +void DLineN :: clear() +{ + long i; + for (i=0;i length-1) { // if delay is too big, + printf("DLineN: Delay length too big ... setting to maximum length of %ld.\n",length-1); + outPoint = inPoint + 1; // force delay to max_length + } + else + outPoint = inPoint - (long) lag; // read chases write + while (outPoint<0) outPoint += length; // modulo maximum length +} + +MY_FLOAT DLineN :: tick(MY_FLOAT sample) // Take one, yield one +{ + inputs[inPoint++] = sample; // Input next sample + if (inPoint == length) // Check for end condition + inPoint -= length; + lastOutput = inputs[outPoint++]; // Read out next value + if (outPoint>=length) // Check for end condition + outPoint -= length; + return lastOutput; +} diff --git a/STK/DLineN.h b/STK/DLineN.h new file mode 100644 index 0000000..6adada8 --- /dev/null +++ b/STK/DLineN.h @@ -0,0 +1,35 @@ +/*******************************************/ +/* Non-Interpolating Delay Line */ +/* Object by Perry R. Cook 1995-96. */ +/* Revised by Gary Scavone, 1999. */ +/* */ +/* This one uses either a delay line of */ +/* maximum length specified on creation */ +/* or a default length of 2048 samples. */ +/* A non-interpolating delay line is */ +/* typically used in non-time varying */ +/* (reverb) applications. */ +/*******************************************/ + +#if !defined(__DLineN_h) +#define __DLineN_h + +#include "Filter.h" + +class DLineN : public Filter +{ + protected: + long inPoint; + long outPoint; + long length; + public: + DLineN(); + DLineN(long max_length); + ~DLineN(); + void clear(); + void setDelay(MY_FLOAT length); + MY_FLOAT tick(MY_FLOAT sample); +}; + +#endif + diff --git a/Debug/.placeholder b/STK/Debug/.placeholder similarity index 100% rename from Debug/.placeholder rename to STK/Debug/.placeholder diff --git a/DrumSynt.cpp b/STK/DrumSynt.cpp similarity index 83% rename from DrumSynt.cpp rename to STK/DrumSynt.cpp index 9be2d79..b9d7072 100644 --- a/DrumSynt.cpp +++ b/STK/DrumSynt.cpp @@ -1,20 +1,22 @@ /*******************************************/ /* Master Class for Drum Synthesizer */ -/* by Perry R. Cook, 1995-96 */ +/* by Perry R. Cook, 1995-96 */ +/* */ /* This instrument contains a bunch of */ -/* RawWave objects, run through a bunch */ +/* RawWvIn objects, run through a bunch */ /* of one-pole filters. All the */ /* corresponding rawwave files have been */ /* sampled at 22050 Hz. Thus, if the */ /* compile-time SRATE = 22050, then */ -/* RawShot objects are used. Otherwise, */ -/* RawInterp objects are used. You can */ -/* specify the maximum Polyphony (maximum */ -/* number of simultaneous voices) in a */ -/* #define in the .h file. */ +/* no interpolation is used. Otherwise, */ +/* the rawwave data is appropriately */ +/* interpolated for the current SRATE. */ +/* You can specify the maximum Polyphony */ +/* (maximum number of simultaneous voices)*/ +/* in a #define in the .h file. */ /* */ -/* Modified for RawWave abstract class */ -/* by Gary P. Scavone (11/11/98) */ +/* Modified for RawWvIn class */ +/* by Gary P. Scavone (4/99) */ /*******************************************/ #include "DrumSynt.h" @@ -78,13 +80,14 @@ DrumSynt :: ~DrumSynt() for ( i=0; isetRate((MY_FLOAT) (22050.0/SRATE)); } waves[numSounding-1]->normalize((MY_FLOAT) 0.4); @@ -142,6 +145,16 @@ void DrumSynt :: noteOn(MY_FLOAT freq, MY_FLOAT amp) #endif } +void DrumSynt :: noteOff(MY_FLOAT amp) +{ /* Set all sounding wave filter gains low */ + int i = 0; + + while(isetGain(amp*0.01); + i++; + } +} + MY_FLOAT DrumSynt :: tick() { int j, i = 0; diff --git a/DrumSynt.h b/STK/DrumSynt.h similarity index 58% rename from DrumSynt.h rename to STK/DrumSynt.h index 3d4ae0d..a23b8ec 100644 --- a/DrumSynt.h +++ b/STK/DrumSynt.h @@ -1,29 +1,29 @@ /*******************************************/ /* Master Class for Drum Synthesizer */ -/* by Perry R. Cook, 1995-96 */ +/* by Perry R. Cook, 1995-96 */ +/* */ /* This instrument contains a bunch of */ -/* RawWave objects, run through a bunch */ +/* RawWvIn objects, run through a bunch */ /* of one-pole filters. All the */ /* corresponding rawwave files have been */ /* sampled at 22050 Hz. Thus, if the */ /* compile-time SRATE = 22050, then */ -/* RawShot objects are used. Otherwise, */ -/* RawInterp objects are used. You can */ -/* specify the maximum Polyphony (maximum */ -/* number of simultaneous voices) in a */ -/* #define in the .h file. */ +/* no interpolation is used. Otherwise, */ +/* the rawwave data is appropriately */ +/* interpolated for the current SRATE. */ +/* You can specify the maximum Polyphony */ +/* (maximum number of simultaneous voices)*/ +/* in a #define in the .h file. */ /* */ -/* Modified for RawWave abstract class */ -/* by Gary P. Scavone (11/11/98) */ +/* Modified for RawWvIn class */ +/* by Gary P. Scavone (4/99) */ /*******************************************/ #if !defined(__DrumSynt_h) #define __DrumSynt_h #include "Instrmnt.h" -#include "RawWave.h" -#include "RawShot.h" -#include "RawInterp.h" +#include "RawWvIn.h" #include "OnePole.h" #define DRUM_NUMWAVES 11 @@ -32,7 +32,7 @@ class DrumSynt : public Instrmnt { protected: - RawWave *waves[DRUM_POLYPHONY]; + RawWvIn *waves[DRUM_POLYPHONY]; OnePole *filters[DRUM_POLYPHONY]; int sounding[DRUM_POLYPHONY]; int numSounding; @@ -40,6 +40,7 @@ class DrumSynt : public Instrmnt DrumSynt(); ~DrumSynt(); virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); + void noteOff(MY_FLOAT amp); virtual MY_FLOAT tick(); }; diff --git a/Envelope.cpp b/STK/Envelope.cpp similarity index 56% rename from Envelope.cpp rename to STK/Envelope.cpp index 31f65cc..9a94e99 100644 --- a/Envelope.cpp +++ b/STK/Envelope.cpp @@ -1,5 +1,6 @@ /*******************************************/ -/* Envelope Class, Perry R. Cook, 1995-96 */ +/* Envelope Class, Perry R. Cook, 1995-96 */ +/* */ /* This is the base class for envelopes. */ /* This one is capable of ramping state */ /* from where it is to a target value by */ @@ -16,10 +17,10 @@ Envelope :: Envelope() : Object() { - target = (MY_FLOAT) 0.0; - value = (MY_FLOAT) 0.0; - rate = (MY_FLOAT) 0.001; - state = 0; + target = (MY_FLOAT) 0.0; + value = (MY_FLOAT) 0.0; + rate = (MY_FLOAT) 0.001; + state = 0; } Envelope :: ~Envelope() @@ -28,77 +29,77 @@ Envelope :: ~Envelope() void Envelope :: keyOn() { - target = (MY_FLOAT) 1.0; - if (value != target) state = 1; + target = (MY_FLOAT) 1.0; + if (value != target) state = 1; } void Envelope :: keyOff() { - target = (MY_FLOAT) 0.0; - if (value != target) state = 1; + target = (MY_FLOAT) 0.0; + if (value != target) state = 1; } void Envelope :: setRate(MY_FLOAT aRate) { - if (aRate < 0.0) { - printf("negative rates not allowed!!, correcting\n"); - rate = -aRate; - } - else rate = aRate; + if (aRate < 0.0) { + printf("negative rates not allowed!!, correcting\n"); + rate = -aRate; + } + else rate = aRate; } void Envelope :: setTime(MY_FLOAT aTime) { - if (aTime < 0.0) { - printf("negative times not allowed!!, correcting\n"); - rate = ONE_OVER_SRATE / -aTime ; - } - else rate = ONE_OVER_SRATE / aTime ; + if (aTime < 0.0) { + printf("negative times not allowed!!, correcting\n"); + rate = ONE_OVER_SRATE / -aTime ; + } + else rate = ONE_OVER_SRATE / aTime ; } void Envelope :: setTarget(MY_FLOAT aTarget) { - target = aTarget; - if (value != target) state = 1; + target = aTarget; + if (value != target) state = 1; } void Envelope :: setValue(MY_FLOAT aValue) { - state = 0; - target = aValue; - value = aValue; + state = 0; + target = aValue; + value = aValue; } MY_FLOAT Envelope :: tick() { - if (state) { - if (target > value) { - value += rate; - if (value >= target) { - value = target; - state = 0; - } - } - else { - value -= rate; - if (value <= target) { - value = target; - state = 0; - } - } + if (state) { + if (target > value) { + value += rate; + if (value >= target) { + value = target; + state = 0; + } } - return value; + else { + value -= rate; + if (value <= target) { + value = target; + state = 0; + } + } + } + return value; } int Envelope :: informTick() { - this->tick(); - return state; + this->tick(); + return state; } MY_FLOAT Envelope :: lastOut() { - return value; + return value; } /************ Test Main ************************/ diff --git a/Envelope.h b/STK/Envelope.h similarity index 65% rename from Envelope.h rename to STK/Envelope.h index 44df5dc..08409eb 100644 --- a/Envelope.h +++ b/STK/Envelope.h @@ -19,23 +19,23 @@ class Envelope : public Object { - protected: - MY_FLOAT value; - MY_FLOAT target; - MY_FLOAT rate; - int state; - public: - Envelope(); - virtual ~Envelope(); - void keyOn(); - void keyOff(); - void setRate(MY_FLOAT aRate); - void setTime(MY_FLOAT aTime); - void setTarget(MY_FLOAT aTarget); - void setValue(MY_FLOAT aValue); - MY_FLOAT tick(); - int informTick(); - MY_FLOAT lastOut(); + protected: + MY_FLOAT value; + MY_FLOAT target; + MY_FLOAT rate; + int state; + public: + Envelope(); + virtual ~Envelope(); + void keyOn(); + void keyOff(); + void setRate(MY_FLOAT aRate); + void setTime(MY_FLOAT aTime); + void setTarget(MY_FLOAT aTarget); + void setValue(MY_FLOAT aValue); + MY_FLOAT tick(); + int informTick(); + MY_FLOAT lastOut(); }; #endif diff --git a/FM4Alg3.cpp b/STK/FM4Alg3.cpp similarity index 100% rename from FM4Alg3.cpp rename to STK/FM4Alg3.cpp diff --git a/FM4Alg3.h b/STK/FM4Alg3.h similarity index 100% rename from FM4Alg3.h rename to STK/FM4Alg3.h diff --git a/FM4Alg4.cpp b/STK/FM4Alg4.cpp similarity index 100% rename from FM4Alg4.cpp rename to STK/FM4Alg4.cpp diff --git a/FM4Alg4.h b/STK/FM4Alg4.h similarity index 100% rename from FM4Alg4.h rename to STK/FM4Alg4.h diff --git a/FM4Alg5.cpp b/STK/FM4Alg5.cpp similarity index 52% rename from FM4Alg5.cpp rename to STK/FM4Alg5.cpp index 9db4709..0ba312c 100644 --- a/FM4Alg5.cpp +++ b/STK/FM4Alg5.cpp @@ -21,8 +21,8 @@ FM4Alg5 :: FM4Alg5() : FM4Op() { - /* We still don't make the waves here yet, because */ - /* we still don't know what they will be. */ + /* We still don't make the waves here yet, because */ + /* we still don't know what they will be. */ } FM4Alg5 :: ~FM4Alg5() @@ -31,23 +31,23 @@ FM4Alg5 :: ~FM4Alg5() MY_FLOAT FM4Alg5 :: tick() { - MY_FLOAT temp,temp2; + MY_FLOAT temp,temp2; - temp = gains[1] * adsr[1]->tick() * waves[1]->tick(); - temp = temp * control1; - waves[0]->addPhaseOffset(temp); - waves[3]->addPhaseOffset(twozero->lastOut()); - temp = gains[3] * adsr[3]->tick() * waves[3]->tick(); - twozero->tick(temp); - waves[2]->addPhaseOffset(temp); - temp = ((MY_FLOAT) 1.0 - (control2 * (MY_FLOAT) 0.5)) * - gains[0] * adsr[0]->tick() * waves[0]->tick(); - temp += control2 * (MY_FLOAT) 0.5 * gains[2] * adsr[2]->tick() * waves[2]->tick(); + temp = gains[1] * adsr[1]->tick() * waves[1]->tick(); + temp = temp * control1; + waves[0]->addPhaseOffset(temp); + waves[3]->addPhaseOffset(twozero->lastOut()); + temp = gains[3] * adsr[3]->tick() * waves[3]->tick(); + twozero->tick(temp); + waves[2]->addPhaseOffset(temp); + temp = ((MY_FLOAT) 1.0 - (control2 * (MY_FLOAT) 0.5)) * + gains[0] * adsr[0]->tick() * waves[0]->tick(); + temp += control2 * (MY_FLOAT) 0.5 * gains[2] * adsr[2]->tick() * waves[2]->tick(); - temp2 = vibWave->tick() * modDepth; /* Calculate amplitude mod */ - temp = temp * ((MY_FLOAT) 1.0 + temp2); /* and apply it to output */ + temp2 = vibWave->tick() * modDepth; /* Calculate amplitude mod */ + temp = temp * ((MY_FLOAT) 1.0 + temp2); /* and apply it to output */ - lastOutput = temp * (MY_FLOAT) 0.5; - return lastOutput; + lastOutput = temp * (MY_FLOAT) 0.5; + return lastOutput; } diff --git a/FM4Alg5.h b/STK/FM4Alg5.h similarity index 100% rename from FM4Alg5.h rename to STK/FM4Alg5.h diff --git a/FM4Alg6.cpp b/STK/FM4Alg6.cpp similarity index 100% rename from FM4Alg6.cpp rename to STK/FM4Alg6.cpp diff --git a/FM4Alg6.h b/STK/FM4Alg6.h similarity index 100% rename from FM4Alg6.h rename to STK/FM4Alg6.h diff --git a/FM4Alg8.cpp b/STK/FM4Alg8.cpp similarity index 100% rename from FM4Alg8.cpp rename to STK/FM4Alg8.cpp diff --git a/FM4Alg8.h b/STK/FM4Alg8.h similarity index 100% rename from FM4Alg8.h rename to STK/FM4Alg8.h diff --git a/STK/FM4Op.cpp b/STK/FM4Op.cpp new file mode 100644 index 0000000..cca8a79 --- /dev/null +++ b/STK/FM4Op.cpp @@ -0,0 +1,184 @@ +/*******************************************/ +/* Master Class for 4 Operator FM Synth */ +/* by Perry R. Cook, 1995-96 */ +/* This instrument contains 4 waves, */ +/* 4 adsr, and various state vars. */ +/* */ +/* The basic Chowning/Stanford FM patent */ +/* expired April 1995, but there exist */ +/* follow-on patents, mostly assigned to */ +/* Yamaha. If you are of the type who */ +/* should worry about this (making money) */ +/* worry away. */ +/* */ +/*******************************************/ + +#include "FM4Op.h" +#include "SKINI11.msg" + +FM4Op :: FM4Op() +{ + int i; + MY_FLOAT temp; + MY_FLOAT tempCoeffs[2] = {(MY_FLOAT) 0.0, (MY_FLOAT) -1.0}; + adsr[0] = new ADSR; + adsr[1] = new ADSR; + adsr[2] = new ADSR; + adsr[3] = new ADSR; + twozero = new TwoZero; + + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + vibWave = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); + vibWave->normalize(); + vibWave->setFreq((MY_FLOAT) 6.0); /* should make this random?? */ + modDepth = (MY_FLOAT) 0.0; + /* We don't make the waves here yet, because */ + /* we don't know what they will be. */ + baseFreq = (MY_FLOAT) 440.0; + ratios[0] = (MY_FLOAT) 1.0; + ratios[1] = (MY_FLOAT) 1.0; + ratios[2] = (MY_FLOAT) 1.0; + ratios[3] = (MY_FLOAT) 1.0; + gains[0] = (MY_FLOAT) 1.0; + gains[1] = (MY_FLOAT) 1.0; + gains[2] = (MY_FLOAT) 1.0; + gains[3] = (MY_FLOAT) 1.0; + twozero->setZeroCoeffs(tempCoeffs); + twozero->setGain((MY_FLOAT) 0.0); + control1 = (MY_FLOAT) 1.0; + control2 = (MY_FLOAT) 1.0; + temp = (MY_FLOAT) 1.0; + for (i=99;i>=0;i--) { + __FM4Op_gains[i] = temp; + temp *= (MY_FLOAT) 0.933033; + } + temp = (MY_FLOAT) 1.0; + for (i=15;i>=0;i--) { + __FM4Op_susLevels[i] = temp; + temp *= (MY_FLOAT) 0.707101; + } + temp = (MY_FLOAT) 8.498186; + for (i=0;i<32;i++) { + __FM4Op_attTimes[i] = temp; + temp *= (MY_FLOAT) 0.707101; + } +} + +FM4Op :: ~FM4Op() +{ + delete adsr[0]; + delete adsr[1]; + delete adsr[2]; + delete adsr[3]; + delete waves[0]; + delete waves[1]; + delete waves[2]; + delete waves[3]; + delete vibWave; + delete twozero; +} + +void FM4Op :: loadWaves(char* wave1, char* wave2, char* wave3, char* wave4) +{ + int i; + waves[0] = new RawWvIn(wave1,"looping"); + waves[1] = new RawWvIn(wave2,"looping"); + waves[2] = new RawWvIn(wave3,"looping"); + waves[3] = new RawWvIn(wave4,"looping"); + for (i=0;i<4;i++) { + waves[i]->normalize(); + } +} +void FM4Op :: setFreq(MY_FLOAT frequency) +{ + baseFreq = frequency; + waves[0]->setFreq(baseFreq * ratios[0]); + waves[1]->setFreq(baseFreq * ratios[1]); + waves[2]->setFreq(baseFreq * ratios[2]); + waves[3]->setFreq(baseFreq * ratios[3]); +} + +void FM4Op :: setRatio(int whichOne, MY_FLOAT ratio) +{ + ratios[whichOne] = ratio; + if (ratio>0.0) + waves[whichOne]->setFreq(baseFreq * ratio); + else + waves[whichOne]->setFreq(ratio); +} + +void FM4Op :: setGain(int whichOne, MY_FLOAT gain) +{ + gains[whichOne]=gain; +} + +void FM4Op :: keyOn() +{ + adsr[0]->keyOn(); + adsr[1]->keyOn(); + adsr[2]->keyOn(); + adsr[3]->keyOn(); +} + +void FM4Op :: keyOff() +{ + adsr[0]->keyOff(); + adsr[1]->keyOff(); + adsr[2]->keyOff(); + adsr[3]->keyOff(); +} + +void FM4Op :: noteOff(MY_FLOAT amp) +{ + this->keyOff(); +#if defined(_debug_) + printf("FM4Op : NoteOff: Amp=%lf\n",amp); +#endif +} + +void FM4Op :: setModulationSpeed(MY_FLOAT mSpeed) +{ + vibWave->setFreq(mSpeed); +} + +void FM4Op :: setModulationDepth(MY_FLOAT mDepth) +{ + modDepth = mDepth; +} + +void FM4Op :: setControl1(MY_FLOAT cVal) +{ + control1 = cVal * (MY_FLOAT) 2.0; +} + +void FM4Op :: setControl2(MY_FLOAT cVal) +{ + control2 = cVal * (MY_FLOAT) 2.0; +} + +void FM4Op :: controlChange(int number, MY_FLOAT value) +{ +#if defined(_debug_) + printf("FM4Op : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_Breath_) /* Control Change 2 */ + this->setControl1(value * NORM_7); + else if (number == __SK_FootControl_) /* Control Change 4 */ + this->setControl2(value * NORM_7); + else if (number == __SK_ModFrequency_) /* Control Change 11 */ + this->setModulationSpeed(value * NORM_7 * (MY_FLOAT) 12.0); /* 0 to 12 Hz */ + else if (number == __SK_ModWheel_) /* Control Change 1 */ + this->setModulationDepth(value * NORM_7); + else if (number == __SK_AfterTouch_Cont_) { + //adsr[0]->setTarget(value * NORM_7); + adsr[1]->setTarget(value * NORM_7); + //adsr[2]->setTarget(value * NORM_7); + adsr[3]->setTarget(value * NORM_7); + } + else { + printf("FM4Op : Undefined Control Number!!\n"); + } +} + diff --git a/STK/FM4Op.h b/STK/FM4Op.h new file mode 100644 index 0000000..d47ab13 --- /dev/null +++ b/STK/FM4Op.h @@ -0,0 +1,59 @@ +/*******************************************/ +/* Master Class for 4 Operator FM Synth */ +/* by Perry R. Cook, 1995-96 */ +/* This instrument contains an 4 waves, */ +/* 4 envelopes, and various state vars. */ +/* */ +/* The basic Chowning/Stanford FM patent */ +/* expired April 1995, but there exist */ +/* follow-on patents, mostly assigned to */ +/* Yamaha. If you are of the type who */ +/* should worry about this (making money) */ +/* worry away. */ +/* */ +/*******************************************/ + +#if !defined(__FM4Op_h) +#define __FM4Op_h + +#include "Instrmnt.h" +#include "ADSR.h" +#include "RawWvIn.h" +#include "TwoZero.h" + +class FM4Op : public Instrmnt +{ + protected: + ADSR *adsr[4]; + RawWvIn *waves[4]; + RawWvIn *vibWave; + TwoZero *twozero; + MY_FLOAT baseFreq; + MY_FLOAT ratios[4]; + MY_FLOAT gains[4]; + MY_FLOAT modDepth; + MY_FLOAT control1; + MY_FLOAT control2; + MY_FLOAT __FM4Op_gains[100]; + MY_FLOAT __FM4Op_susLevels[16]; + MY_FLOAT __FM4Op_attTimes[32]; + public: + FM4Op(); + virtual ~FM4Op(); + void loadWaves(char* wave1, char* wave2, char* wave3, char* wave4); + void clear(); + void setFreq(MY_FLOAT frequency); + void setRatio(int whichOne, MY_FLOAT ratio); + void setGain(int whichOne, MY_FLOAT gain); + void keyOn(); + void keyOff(); + void noteOff(MY_FLOAT amp); + /* There's no tick() method here, because that depends on the algorithm */ + void setModulationSpeed(MY_FLOAT mSpeed); + void setModulationDepth(MY_FLOAT mDepth); + void setControl1(MY_FLOAT cVal); + void setControl2(MY_FLOAT cVal); + virtual void controlChange(int number, MY_FLOAT value); +}; + +#endif diff --git a/FMVoices.cpp b/STK/FMVoices.cpp similarity index 83% rename from FMVoices.cpp rename to STK/FMVoices.cpp index 8e2eb39..950cd69 100644 --- a/FMVoices.cpp +++ b/STK/FMVoices.cpp @@ -10,10 +10,19 @@ FMVoices :: FMVoices() : FM4Alg6() { - this->loadWaves("rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw"); + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file1[128]; + char file2[128]; + char file3[128]; + char file4[128]; + strcpy(file1, RAWWAVE_PATH); + strcpy(file2, RAWWAVE_PATH); + strcpy(file3, RAWWAVE_PATH); + strcpy(file4, RAWWAVE_PATH); + this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"), + strcat(file2,"rawwaves/sinewave.raw"), + strcat(file3,"rawwaves/sinewave.raw"), + strcat(file4,"rawwaves/fwavblnk.raw")); this->setRatio(0,(MY_FLOAT) 2.00); this->setRatio(1,(MY_FLOAT) 4.00); @@ -52,20 +61,20 @@ void FMVoices :: setFreq(MY_FLOAT frequency) MY_FLOAT temp,temp2; int tempi,tempi2; - if (currentVowel < 16) { + if (currentVowel < 32) { tempi2 = currentVowel; temp2 = (MY_FLOAT) 0.9; } - else if (currentVowel < 32) { - tempi2 = currentVowel - 16; + else if (currentVowel < 64) { + tempi2 = currentVowel - 32; temp2 = (MY_FLOAT) 1.0; } - else if (currentVowel < 48) { - tempi2 = currentVowel - 32; + else if (currentVowel < 96) { + tempi2 = currentVowel - 64; temp2 = (MY_FLOAT) 1.1; } - else if (currentVowel <= 64) { - tempi2 = currentVowel - 48; + else if (currentVowel <= 128) { + tempi2 = currentVowel - 96; temp2 = (MY_FLOAT) 1.2; } baseFreq = frequency; @@ -106,7 +115,7 @@ void FMVoices :: controlChange(int number, MY_FLOAT value) if (number == __SK_Breath_) gains[3] = __FM4Op_gains[(int) (value * 0.78125)]; else if (number == __SK_FootControl_) { - tempi = (int) (value / 2); + tempi = (int) value; currentVowel = tempi; this->setFreq(baseFreq); } diff --git a/FMVoices.h b/STK/FMVoices.h similarity index 100% rename from FMVoices.h rename to STK/FMVoices.h diff --git a/Filter.cpp b/STK/Filter.cpp similarity index 100% rename from Filter.cpp rename to STK/Filter.cpp diff --git a/Filter.h b/STK/Filter.h similarity index 100% rename from Filter.h rename to STK/Filter.h diff --git a/STK/Flute.cpp b/STK/Flute.cpp new file mode 100644 index 0000000..ddffaf8 --- /dev/null +++ b/STK/Flute.cpp @@ -0,0 +1,178 @@ +/******************************************/ +/* WaveGuide Flute ala Karjalainen, */ +/* Smith, Waryznyk, etc. */ +/* with polynomial Jet ala Cook */ +/* by Perry Cook, 1995-96 */ +/* */ +/* This is a waveguide model, and thus */ +/* relates to various Stanford Univ. */ +/* and possibly Yamaha and other patents.*/ +/* */ +/* Controls: CONTROL1 = jetDelay */ +/* CONTROL2 = noiseGain */ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ +/******************************************/ + +#include "Flute.h" +#include "SKINI11.msg" + +Flute :: Flute(MY_FLOAT lowestFreq) +{ + long length; + length = (long) (SRATE / lowestFreq + 1); + boreDelay = new DLineL(length); + length >>= 1; + jetDelay = new DLineL(length); + jetTable = new JetTabl; + filter = new OnePole; + dcBlock = new DCBlock; + noise = new Noise; + adsr = new ADSR; + + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); + this->clear(); + + boreDelay->setDelay((MY_FLOAT) 100.0); + jetDelay->setDelay((MY_FLOAT) 49.0); + + filter->setPole((MY_FLOAT) 0.7 - ((MY_FLOAT) 0.1 * (MY_FLOAT) 22050.0 / SRATE)); + filter->setGain((MY_FLOAT) -1.0); + vibr->normalize(); + vibr->setFreq((MY_FLOAT) 5.925); + adsr->setAllTimes((MY_FLOAT) 0.005, (MY_FLOAT) 0.01, (MY_FLOAT) 0.8, (MY_FLOAT) 0.010); + endRefl = (MY_FLOAT) 0.5; + jetRefl = (MY_FLOAT) 0.5; + noiseGain = (MY_FLOAT) 0.15; /* Breath pressure random component */ + vibrGain = (MY_FLOAT) 0.05; /* breath periodic vibrato component */ + jetRatio = (MY_FLOAT) 0.32; + + maxPressure = (MY_FLOAT) 0.0; +} + +Flute :: ~Flute() +{ + delete jetDelay; + delete boreDelay; + delete jetTable; + delete filter; + delete dcBlock; + delete noise; + delete adsr; + delete vibr; +} + +void Flute :: clear() +{ + jetDelay->clear(); + boreDelay->clear(); + filter->clear(); + dcBlock->clear(); +} + +void Flute :: setFreq(MY_FLOAT frequency) +{ + MY_FLOAT temp; + lastFreq = frequency * (MY_FLOAT) 0.66666; /* we're overblowing here */ + temp = SRATE / lastFreq - (MY_FLOAT) 2.0; /* Length - approx. filter delay */ + boreDelay->setDelay(temp); /* Length of bore tube */ + jetDelay->setDelay(temp * jetRatio); /* jet delay shorter */ +} + +void Flute :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate) +{ + adsr->setAttackRate(rate); + maxPressure = amplitude / (MY_FLOAT) 0.8; + adsr->keyOn(); +} + +void Flute :: stopBlowing(MY_FLOAT rate) +{ + adsr->setReleaseRate(rate); + adsr->keyOff(); +} + +void Flute :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + this->setFreq(freq); + this->startBlowing((MY_FLOAT) 1.1 + (amp * (MY_FLOAT) 0.20),amp * (MY_FLOAT) 0.02); + outputGain = amp + (MY_FLOAT) 0.001; +#if defined(_debug_) + printf("Flute : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + +void Flute :: noteOff(MY_FLOAT amp) +{ + this->stopBlowing(amp * (MY_FLOAT) 0.02); +#if defined(_debug_) + printf("Flute : NoteOff: Amp=%lf\n",amp); +#endif +} + +void Flute :: setJetRefl(MY_FLOAT refl) +{ + jetRefl = refl; +} + +void Flute :: setEndRefl(MY_FLOAT refl) +{ + endRefl = refl; +} + +void Flute :: setJetDelay(MY_FLOAT aRatio) +{ + MY_FLOAT temp; + temp = SRATE / lastFreq - (MY_FLOAT) 2.0; /* Length - approx. filter delay */ + jetRatio = aRatio; + jetDelay->setDelay(temp * aRatio); /* Scaled by ratio */ +} + +MY_FLOAT Flute :: tick() +{ + MY_FLOAT temp; + MY_FLOAT pressureDiff; + MY_FLOAT randPressure; + MY_FLOAT breathPressure; + + breathPressure = maxPressure * adsr->tick(); /* Breath Pressure */ + randPressure = noiseGain * noise->tick(); /* Random Deviation */ + randPressure += vibrGain * vibr->tick(); /* + breath vibrato */ + randPressure *= breathPressure; /* All scaled by Breath Pressure */ + + temp = filter->tick(boreDelay->lastOut()); + temp = dcBlock->tick(temp); /* Block DC on reflection */ + pressureDiff = breathPressure + randPressure - /* Breath Pressure */ + (jetRefl * temp); /* - reflected */ + pressureDiff = jetDelay->tick(pressureDiff); /* Jet Delay Line */ + pressureDiff = jetTable->lookup(pressureDiff) /* Non-Lin Jet + reflected */ + + (endRefl * temp); + lastOutput = (MY_FLOAT) 0.3 * boreDelay->tick(pressureDiff); /* Bore Delay and "bell" filter */ + + lastOutput *= outputGain; + return lastOutput; + +} + +void Flute :: controlChange(int number, MY_FLOAT value) +{ +#if defined(_debug_) + printf("Flute : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_JetDelay_) + this->setJetDelay((MY_FLOAT) 0.08 + ((MY_FLOAT) 0.48 * value * NORM_7)); + else if (number == __SK_NoiseLevel_) + noiseGain = (value * NORM_7 * (MY_FLOAT) 0.4); + else if (number == __SK_ModFrequency_) + vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); + else if (number == __SK_ModWheel_) + vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4); + else if (number == __SK_AfterTouch_Cont_) + adsr->setTarget(value * NORM_7); + else { + printf("Flute : Undefined Control Number!!\n"); + } +} diff --git a/STK/Flute.h b/STK/Flute.h new file mode 100644 index 0000000..4d73009 --- /dev/null +++ b/STK/Flute.h @@ -0,0 +1,64 @@ +/******************************************/ +/* WaveGuide Flute ala Karjalainen, */ +/* Smith, Waryznyk, etc. */ +/* with polynomial Jet ala Cook */ +/* by Perry Cook, 1995-96 */ +/* */ +/* This is a waveguide model, and thus */ +/* relates to various Stanford Univ. */ +/* and possibly Yamaha and other patents.*/ +/* */ +/* Controls: CONTROL1 = jetDelay */ +/* CONTROL2 = noiseGain */ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ +/******************************************/ + +#if !defined(__Flute_h) +#define __Flute_h + +#include "Instrmnt.h" +#include "JetTabl.h" +#include "DLineL.h" +#include "OnePole.h" +#include "DCBlock.h" +#include "Noise.h" +#include "ADSR.h" +#include "RawWvIn.h" + +class Flute : public Instrmnt +{ + protected: + DLineL *jetDelay; + DLineL *boreDelay; + JetTabl *jetTable; + OnePole *filter; + DCBlock *dcBlock; + Noise *noise; + ADSR *adsr; + RawWvIn *vibr; + MY_FLOAT lastFreq; + MY_FLOAT maxPressure; + MY_FLOAT jetRefl; + MY_FLOAT endRefl; + MY_FLOAT noiseGain; + MY_FLOAT vibrGain; + MY_FLOAT outputGain; + MY_FLOAT jetRatio; + public: + Flute(MY_FLOAT lowestFreq); + ~Flute(); + void clear(); + void startBlowing(MY_FLOAT amplitude,MY_FLOAT rate); + void stopBlowing(MY_FLOAT rate); + virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); + virtual void noteOff(MY_FLOAT amp); + void setJetRefl(MY_FLOAT refl); + void setEndRefl(MY_FLOAT refl); + virtual void setFreq(MY_FLOAT frequency); + virtual MY_FLOAT tick(); + virtual void controlChange(int number, MY_FLOAT value); + void setJetDelay(MY_FLOAT aLength); +}; + +#endif diff --git a/STK/FormSwep.cpp b/STK/FormSwep.cpp new file mode 100644 index 0000000..e4794f9 --- /dev/null +++ b/STK/FormSwep.cpp @@ -0,0 +1,139 @@ +/*******************************************/ +/* Sweepable Formant (2-pole) */ +/* Filter Class, by Perry R. Cook, 1995-96*/ +/* See books on filters to understand */ +/* more about how this works. This drives*/ +/* to a target at speed set by rate. */ +/*******************************************/ + +#include "FormSwep.h" + +FormSwep :: FormSwep() : Filter() +{ + outputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT)); + poleCoeffs[0] = (MY_FLOAT) 0.0; + poleCoeffs[1] = (MY_FLOAT) 0.0; + gain = (MY_FLOAT) 1.0; + freq = (MY_FLOAT) 0.0; + reson = (MY_FLOAT) 0.0; + currentGain = (MY_FLOAT) 1.0; + currentFreq = (MY_FLOAT) 0.0; + currentReson = (MY_FLOAT) 0.0; + targetGain = (MY_FLOAT) 1.0; + targetFreq = (MY_FLOAT) 0.0; + targetReson = (MY_FLOAT) 0.0; + deltaGain = (MY_FLOAT) 0.0; + deltaFreq = (MY_FLOAT) 0.0; + deltaReson = (MY_FLOAT) 0.0; + sweepState = (MY_FLOAT) 0.0; + sweepRate = (MY_FLOAT) 0.002; + dirty = 0; + this->clear(); +} + +FormSwep :: ~FormSwep() +{ + free(outputs); +} + +void FormSwep :: clear() +{ + outputs[0] = (MY_FLOAT) 0.0; + outputs[1] = (MY_FLOAT) 0.0; +} + +void FormSwep :: setPoleCoeffs(MY_FLOAT *coeffs) +{ + dirty = 0; + poleCoeffs[0] = coeffs[0]; + poleCoeffs[1] = coeffs[1]; +} + +void FormSwep :: setFreqAndReson(MY_FLOAT aFreq, MY_FLOAT aReson) +{ + dirty = 0; + reson = aReson; + freq = aFreq; + currentReson = aReson; + currentFreq = aFreq; + poleCoeffs[1] = - (reson * reson); + poleCoeffs[0] = (MY_FLOAT) 2.0 * reson * (MY_FLOAT) cos(TWO_PI * freq / SRATE); +} + +void FormSwep :: setStates(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain) +{ + dirty = 0; + freq = aFreq; + reson = aReson; + gain = aGain; + targetFreq = aFreq; + targetReson = aReson; + targetGain = aGain; + currentFreq = aFreq; + currentReson = aReson; + currentGain = aGain; +} + +void FormSwep :: setTargets(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain) +{ + dirty = 1; + targetFreq = aFreq; + targetReson = aReson; + targetGain = aGain; + deltaFreq = aFreq - currentFreq; + deltaReson = aReson - currentReson; + deltaGain = aGain - currentGain; + sweepState = (MY_FLOAT) 0.0; +} + +void FormSwep :: setSweepRate(MY_FLOAT aRate) +{ + sweepRate = aRate; +} + +void FormSwep :: setSweepTime(MY_FLOAT aTime) +{ + sweepRate = ONE_OVER_SRATE / aTime; +} + +void FormSwep :: setGain(MY_FLOAT aValue) +{ + gain = aValue; +} + +MY_FLOAT FormSwep :: tick(MY_FLOAT sample) // Perform Filter Operation +{ + MY_FLOAT temp; + + if (dirty) { + sweepState += sweepRate; + if (sweepState>= 1.0) { + sweepState = (MY_FLOAT) 1.0; + dirty = 0; + currentReson = targetReson; + reson = targetReson; + currentFreq = targetFreq; + freq = targetFreq; + currentGain = targetGain; + gain = targetGain; + } + else { + currentReson = reson + (deltaReson * sweepState); + currentFreq = freq + (deltaFreq * sweepState); + currentGain = gain + (deltaGain * sweepState); + } + poleCoeffs[1] = - (currentReson * currentReson); + poleCoeffs[0] = (MY_FLOAT) 2.0 * currentReson * + (MY_FLOAT) cos(TWO_PI * currentFreq / SRATE); + } + + temp = currentGain * sample; + temp += poleCoeffs[0] * outputs[0]; + temp += poleCoeffs[1] * outputs[1]; + outputs[1] = outputs[0]; + outputs[0] = temp; + lastOutput = outputs[0]; + return lastOutput; +} + + diff --git a/STK/FormSwep.h b/STK/FormSwep.h new file mode 100644 index 0000000..3ef04b7 --- /dev/null +++ b/STK/FormSwep.h @@ -0,0 +1,46 @@ +/*******************************************/ +/* Sweepable Formant (2-pole) */ +/* Filter Class, by Perry R. Cook, 1995-96*/ +/* See books on filters to understand */ +/* more about how this works. Nothing */ +/* out of the ordinary in this version. */ +/*******************************************/ + +#if !defined(__FormSwep_h) +#define __FormSwep_h + +#include "Filter.h" + +class FormSwep : public Filter +{ + protected: + MY_FLOAT poleCoeffs[2]; + MY_FLOAT freq; + MY_FLOAT reson; + int dirty; + MY_FLOAT targetFreq; + MY_FLOAT targetReson; + MY_FLOAT targetGain; + MY_FLOAT currentFreq; + MY_FLOAT currentReson; + MY_FLOAT currentGain; + MY_FLOAT deltaFreq; + MY_FLOAT deltaReson; + MY_FLOAT deltaGain; + MY_FLOAT sweepState; + MY_FLOAT sweepRate; + public: + FormSwep(); + ~FormSwep(); + void clear(); + void setPoleCoeffs(MY_FLOAT *coeffs); + void setGain(MY_FLOAT aValue); + void setFreqAndReson(MY_FLOAT aFreq, MY_FLOAT aReson); + void setStates(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain); + void setTargets(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain); + void setSweepRate(MY_FLOAT aRate); + void setSweepTime(MY_FLOAT aTime); + MY_FLOAT tick(MY_FLOAT sample); +}; + +#endif diff --git a/STK/HeavyMtl.cpp b/STK/HeavyMtl.cpp new file mode 100644 index 0000000..48becb8 --- /dev/null +++ b/STK/HeavyMtl.cpp @@ -0,0 +1,68 @@ +/******************************************/ +/* Heavy Metal Synth Subclass */ +/* of Algorithm 3 (TX81Z) Subclass of */ +/* 3 Operator FM Synth */ +/* by Perry R. Cook, 1995-96 */ +/******************************************/ + +#include "HeavyMtl.h" + +HeavyMtl :: HeavyMtl() : FM4Alg3() +{ + char file1[128]; + char file2[128]; + char file3[128]; + char file4[128]; + strcpy(file1, RAWWAVE_PATH); + strcpy(file2, RAWWAVE_PATH); + strcpy(file3, RAWWAVE_PATH); + strcpy(file4, RAWWAVE_PATH); + this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"), + strcat(file2,"rawwaves/sinewave.raw"), + strcat(file3,"rawwaves/sinewave.raw"), + strcat(file4,"rawwaves/fwavblnk.raw")); + + this->setRatio(0,(MY_FLOAT) (1.00 * 1.000)); + this->setRatio(1,(MY_FLOAT) (4.00 * 0.999)); + this->setRatio(2,(MY_FLOAT) (3.00 * 1.001)); + this->setRatio(3,(MY_FLOAT) (0.50 * 1.002)); + gains[0] = __FM4Op_gains[92]; + gains[1] = __FM4Op_gains[76]; + gains[2] = __FM4Op_gains[91]; + gains[3] = __FM4Op_gains[68]; + adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.001,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01); + adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.010,(MY_FLOAT) 1.0,(MY_FLOAT) 0.50); + adsr[2]->setAllTimes((MY_FLOAT) 0.010,(MY_FLOAT) 0.005,(MY_FLOAT) 1.0,(MY_FLOAT) 0.20); + adsr[3]->setAllTimes((MY_FLOAT) 0.030,(MY_FLOAT) 0.010,(MY_FLOAT) 0.2,(MY_FLOAT) 0.20); + twozero->setGain((MY_FLOAT) 2.0); + vibWave->setFreq((MY_FLOAT) 5.5); + modDepth = (MY_FLOAT) 0.00; +} + +HeavyMtl :: ~HeavyMtl() +{ + +} + +void HeavyMtl :: setFreq(MY_FLOAT frequency) +{ + baseFreq = frequency; + waves[0]->setFreq(baseFreq * ratios[0]); + waves[1]->setFreq(baseFreq * ratios[1]); + waves[2]->setFreq(baseFreq * ratios[2]); + waves[3]->setFreq(baseFreq * ratios[3]); +} + +void HeavyMtl :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + gains[0] = amp * __FM4Op_gains[92]; + gains[1] = amp * __FM4Op_gains[76]; + gains[2] = amp * __FM4Op_gains[91]; + gains[3] = amp * __FM4Op_gains[68]; + this->setFreq(freq); + this->keyOn(); +#if defined(_debug_) + printf("HeavyMtl : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + diff --git a/HeavyMtl.h b/STK/HeavyMtl.h similarity index 100% rename from HeavyMtl.h rename to STK/HeavyMtl.h diff --git a/Instrmnt.cpp b/STK/Instrmnt.cpp similarity index 100% rename from Instrmnt.cpp rename to STK/Instrmnt.cpp diff --git a/Instrmnt.h b/STK/Instrmnt.h similarity index 100% rename from Instrmnt.h rename to STK/Instrmnt.h diff --git a/JCRev.cpp b/STK/JCRev.cpp similarity index 100% rename from JCRev.cpp rename to STK/JCRev.cpp diff --git a/JCRev.h b/STK/JCRev.h similarity index 100% rename from JCRev.h rename to STK/JCRev.h diff --git a/JetTabl.cpp b/STK/JetTabl.cpp similarity index 100% rename from JetTabl.cpp rename to STK/JetTabl.cpp diff --git a/JetTabl.h b/STK/JetTabl.h similarity index 91% rename from JetTabl.h rename to STK/JetTabl.h index 1dd7f0b..931de3a 100644 --- a/JetTabl.h +++ b/STK/JetTabl.h @@ -7,6 +7,9 @@ /* nomial calculation. */ /**********************************************/ +#if !defined(__JetTabl_h) +#define __JetTabl_h + #include "Object.h" class JetTabl : public Object @@ -20,3 +23,4 @@ class JetTabl : public Object MY_FLOAT lastOut(); }; +#endif diff --git a/LipFilt.cpp b/STK/LipFilt.cpp similarity index 100% rename from LipFilt.cpp rename to STK/LipFilt.cpp diff --git a/LipFilt.h b/STK/LipFilt.h similarity index 100% rename from LipFilt.h rename to STK/LipFilt.h diff --git a/MD2SKINI.cpp b/STK/MD2SKINI.cpp similarity index 59% rename from MD2SKINI.cpp rename to STK/MD2SKINI.cpp index e921d96..a63c3c7 100644 --- a/MD2SKINI.cpp +++ b/STK/MD2SKINI.cpp @@ -2,6 +2,7 @@ /* Simple Realtime MIDI to SKINI Parser */ /* Gary P. Scavone, February 1998. */ /* Revised for sockets, May & June 1998. */ +/* SKINI/MIDI merge added August 1999. */ /* */ /* This object takes MIDI from the input */ /* stream (via the MIDIIO class), */ @@ -9,177 +10,213 @@ /* messages. */ /*******************************************/ -#include "miditabl.h" #include "MIDIIO.h" #include "SKINI11.msg" +#if defined(__STK_REALTIME_) + int outAHere = 0; -#if defined(__SGI_REALTIME_) +// Do OS dependent declarations and includes +#if defined(__OS_IRIX_) #include #include #include +#include +#include +#include #include pid_t exit_thread; -void newString(void *) -{ - char inputString[128]; - printf("Type 'ex' to quit.\n"); - while (!outAHere) { - fgets(inputString, 128, stdin); - if (inputString[0] == 'e' && inputString[1] == 'x') { - outAHere = 1; - } - else printf("Type 'ex' to quit.\n"); - } -} - -#elif defined(__USS_REALTIME_) +#elif defined(__OS_Linux_) #include +#include +#include +#include +#include +#include pthread_t exit_thread; -void *newString(void *) -{ - char inputString[128]; - printf("Type 'ex' to quit.\n"); - while (!outAHere) { - fgets(inputString, 128, stdin); - if (inputString[0] == 'e' && inputString[1] == 'x') { - outAHere = 1; - } - else printf("Type 'ex' to quit.\n"); - } -} - -#elif (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) +#elif defined(__OS_Win_) #include #include unsigned long exit_thread; -void newString(void *) -{ - char inputString[128]; - printf("Type 'ex' to quit.\n"); - while (!outAHere) { - fgets(inputString, 128, stdin); - if (inputString[0] == 'e' && inputString[1] == 'x') { - outAHere = 1; - } - else printf("Type 'ex' to quit.\n"); - } -} +#endif + +// The thread function definition protocols are slightly +// different under Irix, Linux, and Windoze. +#if defined(__OS_IRIX_) + +void monitorStdin(void *) + +#elif defined(__OS_Linux_) + +void *monitorStdin(void *) + +#elif defined(__OS_Win_) + +void monitorStdin(void *) #endif +{ + char inputString[128]; + printf("Type 'Exit' to quit.\n"); + while (!outAHere) { + fgets(inputString, 128, stdin); + if (inputString[3] == 't' && inputString[1] == 'x' + && inputString[2] == 'i' && inputString[0] == 'E') { + outAHere = 1; + } + else { + printf(inputString); + fflush(stdout); + } + } +} + + void errorf(void) { - printf("useage: MD2SKINI \n"); - printf(" where the optional specifies a file\n"); - printf(" to which the SKINI output stream is written.\n"); - printf(" The SKINI output stream is always written to stdout,\n"); - printf(" whether an output file is specified or not.\n"); + printf("useage: MD2SKINI \n\n"); + printf(" With no arguments, MD2SKINI converts MIDI input to SKINI\n"); + printf(" format and sends the output directly to stdout.\n"); + printf(" With flag = -s , the output is sent over a socket\n"); + printf(" connection to the optional hostname (default = localhost).\n"); + printf(" With flag = -f , the output stream is simultaneously\n"); + printf(" written to the file specified by the optional \n"); + printf(" (default = test.ski).\n\n"); exit(0); } void main(int argc,char *argv[]) { - long j; + long j, i = 1; MY_FLOAT byte2, byte3; int channel; int firstMessage = 1; int writeFileOut = 0; FILE *fileOut; MIDIIO *controller; + char hostName[256]; + char fileName[256]; + int useSocket = 0; + int theSocket; + struct sockaddr_in saServer; - if (argc>2) { + if (argc>5) { errorf(); } - if (argc == 2) { - fileOut = fopen(argv[1],"wb"); - writeFileOut = 1; + // Parse the command-line arguments. + while (i < argc) { + if (argv[i][0] == '-') { + switch(argv[i][1]) { + + case 's': + if ((i+1 < argc) && argv[i+1][0] != '-') { + i++; + strcpy(hostName,argv[i]); + } + else strcpy(hostName,"localhost"); + useSocket = 1; + break; + + case 'f': + if ((i+1 < argc) && argv[i+1][0] != '-') { + i++; + strcpy(fileName,argv[i]); + if (strstr(fileName,".ski") == NULL) strcat(fileName,".ski"); + } + else strcpy(fileName,"test.ski"); + fileOut = fopen(fileName,"wb"); + writeFileOut = 1; + break; + + default: + errorf(); + break; + } + } + else errorf(); + i++; } MY_FLOAT dt=0.0; - controller = new MIDIIO(); - /* Setup the exit thread. */ -#if defined(__SGI_REALTIME_) - exit_thread = sproc(newString, PR_SALL); - if (exit_thread == -1) { - fprintf(stderr, "Unable to create exit thread.\n"); - printf("Exiting MD2SKINI process.\n"); - exit(0); - } -#elif defined(__USS_REALTIME_) - int err = 0; - err = pthread_create(&exit_thread, NULL, newString, NULL); - if (err) - { - fprintf(stderr, "Unable to create exit thread.\n"); - printf("Exiting MD2SKINI process.\n"); - exit(0); - } -#elif (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) - exit_thread = _beginthread(newString, 0, NULL); - if (exit_thread == -1) { - fprintf(stderr, "Unable to create exit thread.\n"); - printf("Exiting MD2SKINI process.\n"); - exit(0); - } + // If using sockets, setup the client socket + if (useSocket) { + +#if defined(__OS_Win_) // Stupid Windoze only stuff + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(1,1); + + WSAStartup(wVersionRequested, &wsaData); + if (wsaData.wVersion != wVersionRequested) { + fprintf(stderr,"\n Wrong Windoze socket library version!\n"); + exit(0); + } #endif - /* Setup the client socket */ -#if defined(__SOCKET) - - WORD wVersionRequested = MAKEWORD(1,1); - WSADATA wsaData; - SOCKET theSocket; - int nRet; - - nRet = WSAStartup(wVersionRequested, &wsaData); - if (wsaData.wVersion != wVersionRequested) - { - fprintf(stderr,"\n Wrong version\n"); - outAHere = 1; - WSACleanup(); - exit(0); + // Create the client-side socket + theSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (theSocket < 0) { + fprintf(stderr,"Couldn't create socket ... aborting!\n"); + exit(0); } - theSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (theSocket == INVALID_SOCKET) - { - fprintf(stderr,"socket open failed\n"); - outAHere = 1; - WSACleanup(); - exit(0); + struct hostent *hostp; + + if ((hostp = gethostbyname(hostName)) == 0) { + fprintf(stderr,"%s: unknown host ... aborting!\n", hostName); + exit(0); } - /* Fill in the address structure */ - SOCKADDR_IN saServer; + // Fill in the address structure + saServer.sin_family = AF_INET; + memcpy((void *)&saServer.sin_addr, hostp->h_addr, hostp->h_length); + saServer.sin_port = htons(2001); // Port number - /* Modified to always use the loopback address of 127.0.0.1 */ - saServer.sin_family = AF_INET; - saServer.sin_port = htons(2001); // Port number from command line - saServer.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" ); - - /* connect to the server */ - nRet = connect(theSocket, (LPSOCKADDR)&saServer, sizeof(struct sockaddr)); - if (nRet == SOCKET_ERROR) - { - fprintf(stderr,"socket connect failed\n"); - closesocket(theSocket); - outAHere = 1; + // Connect to the server + if(connect(theSocket, (struct sockaddr *)&saServer, sizeof(saServer)) < 0) { + fprintf(stderr,"Socket connect failed ... aborting!\n"); +#if defined(__OS_Win_) + closesocket(theSocket); WSACleanup(); - exit(0); +#else + close(theSocket); +#endif + exit(0); } + } + + // Setup the exit thread. +#if defined(__OS_IRIX_) + exit_thread = sproc(monitorStdin, PR_SALL); + if (exit_thread == -1) { + fprintf(stderr, "Unable to create exit thread ... aborting.\n"); + exit(0); + } +#elif defined(__OS_Linux_) + int err = 0; + err = pthread_create(&exit_thread, NULL, monitorStdin, NULL); + if (err) { + fprintf(stderr, "Unable to create exit thread ... aborting.\n"); + exit(0); + } +#elif defined(__OS_Win_) + exit_thread = _beginthread(monitorStdin, 0, NULL); + if (exit_thread == -1) { + fprintf(stderr, "Unable to create exit thread ... aborting.\n"); + exit(0); + } #endif /* Write SKINI messages to buffer 's'. This is the easiest way @@ -204,6 +241,7 @@ void main(int argc,char *argv[]) if (byte3 < 1.0) { sprintf(s,"NoteOff\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,64.0); if (writeFileOut) { + fprintf(fileOut,"NoteOff\t\t%.3f %d %.1f %.1f\n",dt,channel,byte2,64.0); } } else { sprintf(s,"NoteOn\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,byte3); @@ -324,20 +362,23 @@ void main(int argc,char *argv[]) break; } -#if defined(__SOCKET) - nRet = send(theSocket, s, strlen(s), 0); - if (nRet == SOCKET_ERROR) - { - fprintf(stderr,"send failed\n"); + if (useSocket) { + if (send(theSocket, s, strlen(s), 0) < 0) { + fprintf(stderr,"Socket connection failed ... aborting.\n"); +#if defined(__OS_Win_) closesocket(theSocket); - outAHere = 1; WSACleanup(); +#else + close(theSocket); +#endif + outAHere = 1; exit(0); } -#else - printf("%s", s); - fflush(stdout); -#endif + } + else { + printf("%s", s); + fflush(stdout); + } memset(s, 0, sizeof(s)); #if defined(__OS_Win_) } else Sleep ( (DWORD) 2); @@ -347,23 +388,24 @@ void main(int argc,char *argv[]) } sprintf(s,"Exiting MD2SKINI process ... bye!\n"); -#if defined(__SOCKET) - nRet = send(theSocket, s, strlen(s), 0); - closesocket(theSocket); - WSACleanup(); + if (useSocket) { + send(theSocket, s, strlen(s), 0); +#if defined(__OS_Win_) + closesocket(theSocket); + WSACleanup(); #else - printf("%s", s); - fflush(stdout); + close(theSocket); #endif - + } + else { + printf("%s", s); + fflush(stdout); + } if (writeFileOut) { - printf("Wrote SKINI output to file %s.\n", argv[1]); + printf("Wrote SKINI output to file %s.\n", fileName); fclose(fileOut); } delete controller; - -#if defined(__SGI_REALTIME_) - kill(exit_thread, SIGKILL); -#endif } +#endif diff --git a/MIDIIO.cpp b/STK/MIDIIO.cpp similarity index 77% rename from MIDIIO.cpp rename to STK/MIDIIO.cpp index 696e9a8..0c57f61 100644 --- a/MIDIIO.cpp +++ b/STK/MIDIIO.cpp @@ -21,15 +21,16 @@ #include "MIDIIO.h" +#if defined(__STK_REALTIME_) + #define MIDI_BUFFER_SIZE 1024 int writeOffset; int readOffset; - -#if defined(__SGI_REALTIME_) +#if defined(__OS_IRIX_) /*************************************/ -/* __SGI_REALTIME_ */ +/* SGI MIDI INPUT */ /*************************************/ #include @@ -150,10 +151,10 @@ int MIDIIO :: nextMessage() } -#elif defined(__USS_REALTIME_) +#elif defined(__OSS_API_) /*************************************/ -/* __USS_REALTIME_ */ +/* OSS MIDI INPUT */ /*************************************/ #include @@ -203,93 +204,95 @@ void *midiInputThread(void *) exit(0); } - switch ( InBytes[0] ) - { - case SEQ_WAIT: - /* MIDI clock ticks ... the first MIDI message deltaTime is calculated - * with respect to the start of the MIDI clock. - */ - newTime = ((InBytes[3]<<16)|(InBytes[2]<<8)| InBytes[1]); - break; - - case SEQ_ECHO: - /* no echo events yet defined */ -#ifdef DEBUG - fprintf(stderr,"ECHO EVENT\n"); -#endif - break; - - case SEQ_MIDIPUTC: - /* Determination of a full MIDI message from the input MIDI stream is based - here on the observation that MIDI status bytes and subsequent data bytes - are NOT returned in the same read() call. Rather, they are spread out - over multiple read() returns, with only a single value per return. So, - if we find a status byte, we then determine the number of expected - operands and process that number of subsequent read()s to determine the - complete MIDI message. + if (n == 4) { /* ignore reads returning less than 4 bytes */ + switch ( InBytes[0] ) + { + case SEQ_WAIT: + /* MIDI clock ticks ... the first MIDI message deltaTime is calculated + * with respect to the start of the MIDI clock. */ - if (InBytes[1] & 0x80) { /* Status Byte */ - if (MessageType(InBytes[1]) == MIDI_SYSTEM_MSG) - { - NumArgs = 0; /* no timing info */ + newTime = ((InBytes[3]<<16)|(InBytes[2]<<8)| InBytes[1]); + break; + + case SEQ_ECHO: + /* no echo events yet defined */ #ifdef DEBUG - fprintf(stderr, "SYSTEM MESSAGE\n"); + fprintf(stderr,"ECHO EVENT\n"); #endif - } - else if (MessageType(InBytes[1]) == MIDI_PGM_CHANGE || - MessageType(InBytes[1]) == MIDI_CHN_PRESSURE) - { - NumArgs = 1; - } - else - { - NumArgs = 2; - } - newMessage.data[0] = InBytes[1]; - ArgsLeft = NumArgs; - newMessage.data[1] = 0; - newMessage.data[2] = 0; - } + break; - if (ArgsLeft && !(InBytes[1] & 0x80)) { /* not a status byte */ - if (ArgsLeft == NumArgs) - newMessage.data[1] = InBytes[1]; - else - { - newMessage.data[2] = InBytes[1]; - } - - --ArgsLeft; - - /* If MIDI message complete, then setup for running status mode - (another event of the same type without status byte). - */ - if ( !ArgsLeft ) { - if (MessageType(newMessage.data[0]) == (int) MIDI_PGM_CHANGE || - MessageType(newMessage.data[0]) == (int) MIDI_CHN_PRESSURE) + case SEQ_MIDIPUTC: + /* Determination of a full MIDI message from the input MIDI stream is based + here on the observation that MIDI status bytes and subsequent data bytes + are NOT returned in the same read() call. Rather, they are spread out + over multiple read() returns, with only a single value per return. So, + if we find a status byte, we then determine the number of expected + operands and process that number of subsequent read()s to determine the + complete MIDI message. + */ + if (InBytes[1] & 0x80) { /* Status Byte */ + if (MessageType(InBytes[1]) == MIDI_SYSTEM_MSG) { - ArgsLeft = 1; + NumArgs = 0; /* no timing info */ +#ifdef DEBUG + fprintf(stderr, "SYSTEM MESSAGE\n"); +#endif } + else if (MessageType(InBytes[1]) == MIDI_PGM_CHANGE || + MessageType(InBytes[1]) == MIDI_CHN_PRESSURE) + { + NumArgs = 1; + } + else + { + NumArgs = 2; + } + newMessage.data[0] = InBytes[1]; + ArgsLeft = NumArgs; + newMessage.data[1] = 0; + newMessage.data[2] = 0; + } + + if (ArgsLeft && !(InBytes[1] & 0x80)) { /* not a status byte */ + if (ArgsLeft == NumArgs) + newMessage.data[1] = InBytes[1]; else { - ArgsLeft = 2; + newMessage.data[2] = InBytes[1]; } - newMessage.time = newTime - lastTime; - lastTime = newTime; - // Put newMessage in the circular buffer - midiBuffer[writeOffset] = newMessage; - writeOffset++; + --ArgsLeft; - if( writeOffset >= MIDI_BUFFER_SIZE ) - writeOffset = 0; - break; + /* If MIDI message complete, then setup for running status mode + (another event of the same type without status byte). + */ + if ( !ArgsLeft ) { + if (MessageType(newMessage.data[0]) == (int) MIDI_PGM_CHANGE || + MessageType(newMessage.data[0]) == (int) MIDI_CHN_PRESSURE) + { + ArgsLeft = 1; + } + else + { + ArgsLeft = 2; + } + newMessage.time = newTime - lastTime; + lastTime = newTime; + + // Put newMessage in the circular buffer + midiBuffer[writeOffset] = newMessage; + writeOffset++; + + if( writeOffset >= MIDI_BUFFER_SIZE ) + writeOffset = 0; + break; + } } - } - default: - break; - } + default: + break; + } + } } } @@ -324,6 +327,7 @@ MIDIIO :: MIDIIO() MIDIIO :: ~MIDIIO() { + pthread_cancel(midi_input_thread); if (_seqfd != 0) close(_seqfd); delete [] midiBuffer; } @@ -331,7 +335,6 @@ MIDIIO :: ~MIDIIO() int MIDIIO::nextMessage() { MIDIMESSAGE lastEvent; - unsigned long micros = 2000; if ( readOffset == writeOffset ) return 0; @@ -353,10 +356,10 @@ int MIDIIO::nextMessage() } -#elif (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) +#elif defined(__OS_Win_) /*************************************/ -/* __WIN_REALTIME_ */ +/* Windoze MIDI INPUT */ /*************************************/ #include @@ -509,6 +512,7 @@ int MIDIIO :: nextMessage() byte1 = (int) (lastEvent.data & 0xff00) >> 8; byte2 = (int) (lastEvent.data & 0xff0000) >> 16; channel = (int) (status & 0x0f); + status &= 0xf0; // Clear lower byte of status newTime = lastEvent.time; deltaTime = (float) (newTime - lastTime) * 0.001; lastTime = newTime; @@ -573,3 +577,6 @@ MY_FLOAT MIDIIO :: getDeltaTime() { return deltaTime; } + + +#endif diff --git a/MIDIIO.h b/STK/MIDIIO.h similarity index 95% rename from MIDIIO.h rename to STK/MIDIIO.h index 0e1ab58..94670d7 100644 --- a/MIDIIO.h +++ b/STK/MIDIIO.h @@ -44,7 +44,7 @@ class MIDIIO : public Object MY_FLOAT getDeltaTime(); }; -#if (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) +#if defined(__OS_Win_) #include #include diff --git a/Mandolin.cpp b/STK/Mandolin.cpp similarity index 66% rename from Mandolin.cpp rename to STK/Mandolin.cpp index 8def25e..cd30db8 100644 --- a/Mandolin.cpp +++ b/STK/Mandolin.cpp @@ -2,7 +2,8 @@ /* Commuted Mandolin Subclass of enhanced */ /* dual plucked-string model */ /* by Perry Cook, 1995-96 */ -/* Controls: CONTROL1 = bodySize */ +/* */ +/* Controls: CONTROL1 = bodySize */ /* CONTROL2 = pluckPosition */ /* CONTROL3 = loopGain */ /* MOD_WHEEL= deTuning */ @@ -21,24 +22,32 @@ Mandolin :: Mandolin(MY_FLOAT lowestFreq) : Plucked2(lowestFreq) { - int i; - - soundfile[0] = new RawInterp("rawwaves/mand1.raw"); - soundfile[1] = new RawInterp("rawwaves/mand2.raw"); - soundfile[2] = new RawInterp("rawwaves/mand3.raw"); - soundfile[3] = new RawInterp("rawwaves/mand4.raw"); - soundfile[4] = new RawInterp("rawwaves/mand5.raw"); - soundfile[5] = new RawInterp("rawwaves/mand6.raw"); - soundfile[6] = new RawInterp("rawwaves/mand7.raw"); - soundfile[7] = new RawInterp("rawwaves/mand8.raw"); - soundfile[8] = new RawInterp("rawwaves/mand9.raw"); - soundfile[9] = new RawInterp("rawwaves/mand10.raw"); - soundfile[10] = new RawInterp("rawwaves/mand11.raw"); - soundfile[11] = new RawInterp("rawwaves/mand12.raw"); - for (i=0;i<12;i++) { - // soundfile[i]->normalize((MY_FLOAT) 0.1); /* Empirical hack here */ - soundfile[i]->setLooping(0); - } + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char temp[128]; + strcpy(temp, RAWWAVE_PATH); + soundfile[0] = new RawWvIn(strcat(temp,"rawwaves/mand1.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[1] = new RawWvIn(strcat(temp,"rawwaves/mand2.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[2] = new RawWvIn(strcat(temp,"rawwaves/mand3.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[3] = new RawWvIn(strcat(temp,"rawwaves/mand4.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[4] = new RawWvIn(strcat(temp,"rawwaves/mand5.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[5] = new RawWvIn(strcat(temp,"rawwaves/mand6.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[6] = new RawWvIn(strcat(temp,"rawwaves/mand7.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[7] = new RawWvIn(strcat(temp,"rawwaves/mand8.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[8] = new RawWvIn(strcat(temp,"rawwaves/mand9.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[9] = new RawWvIn(strcat(temp,"rawwaves/mand10.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[10] = new RawWvIn(strcat(temp,"rawwaves/mand11.raw"),"oneshot"); + strcpy(temp, RAWWAVE_PATH); + soundfile[11] = new RawWvIn(strcat(temp,"rawwaves/mand12.raw"),"oneshot"); directBody = 1.0; mic = 0; dampTime = 0; @@ -59,7 +68,7 @@ void Mandolin :: pluck(MY_FLOAT amplitude) pluckAmp = amplitude; /* Set Pick Position which puts zeroes at pos*length */ combDelay->setDelay((MY_FLOAT) 0.5 * pluckPos * lastLength); - dampTime = (long) lastLength; /* See tick method below */ + dampTime = (long) lastLength; /* See tick method below */ waveDone = 0; } @@ -90,28 +99,28 @@ MY_FLOAT Mandolin :: tick() { MY_FLOAT temp = (MY_FLOAT) 0; if (!waveDone) { - waveDone = soundfile[mic]->informTick(); /* as long as it goes . . . */ - temp = soundfile[mic]->lastOut() * pluckAmp; /* scaled pluck excitation */ - temp = temp - combDelay->tick(temp); /* with comb filtering */ + waveDone = soundfile[mic]->informTick(); /* as long as it goes . . . */ + temp = soundfile[mic]->lastOut() * pluckAmp; /* scaled pluck excitation */ + temp = temp - combDelay->tick(temp); /* with comb filtering */ } - if (dampTime>=0) { /* Damping hack to help avoid */ - dampTime -= 1; /* overflow on replucking */ - lastOutput = delayLine->tick( /* Calculate 1st delay */ - filter->tick( /* filterered reflection */ - temp + /* plus pluck excitation */ - (delayLine->lastOut() * (MY_FLOAT) 0.7))); - lastOutput += delayLine2->tick( /* and 2nd delay */ - filter2->tick( /* just like the 1st */ + if (dampTime>=0) { /* Damping hack to help avoid */ + dampTime -= 1; /* overflow on replucking */ + lastOutput = delayLine->tick( /* Calculate 1st delay */ + filter->tick( /* filterered reflection */ + temp + /* plus pluck excitation */ + (delayLine->lastOut() * (MY_FLOAT) 0.7))); + lastOutput += delayLine2->tick( /* and 2nd delay */ + filter2->tick( /* just like the 1st */ temp + - (delayLine2->lastOut() * (MY_FLOAT) 0.7))); /* that's the whole thing!! */ + (delayLine2->lastOut() * (MY_FLOAT) 0.7))); } else { /* No damping hack after 1 period */ - lastOutput = delayLine->tick( /* Calculate 1st delay */ - filter->tick( /* filtered reflection */ - temp + /* plus pluck excitation */ - (delayLine->lastOut() * loopGain))); - lastOutput += delayLine2->tick( /* and 2nd delay */ - filter2->tick( /* just like the 1st */ + lastOutput = delayLine->tick( /* Calculate 1st delay */ + filter->tick( /* filtered reflection */ + temp + /* plus pluck excitation */ + (delayLine->lastOut() * loopGain))); + lastOutput += delayLine2->tick( /* and 2nd delay */ + filter2->tick( /* just like the 1st */ temp + (delayLine2->lastOut() * loopGain))); } diff --git a/Mandolin.h b/STK/Mandolin.h similarity index 86% rename from Mandolin.h rename to STK/Mandolin.h index 58f3381..09ddbac 100644 --- a/Mandolin.h +++ b/STK/Mandolin.h @@ -1,11 +1,11 @@ -/********************************************/ +/********************************************/ /* Commuted Mandolin Subclass of enhanced */ /* dual plucked-string model */ /* by Perry Cook, 1995-96 */ /* Controls: CONTROL1 = bodySize */ /* CONTROL2 = pluckPosition */ -/* CONTROL3 = loopGain */ -/* MOD_WHEEL= deTuning */ +/* CONTROL3 = loopGain */ +/* MOD_WHEEL= deTuning */ /* */ /* Note: Commuted Synthesis, as with many */ /* other WaveGuide techniques, is covered */ @@ -20,12 +20,12 @@ #define __Mandolin_h #include "Plucked2.h" -#include "RawInterp.h" +#include "RawWvIn.h" class Mandolin : public Plucked2 { protected: - RawInterp *soundfile[12]; + RawWvIn *soundfile[12]; MY_FLOAT directBody; int mic; long dampTime; diff --git a/STK/Marimba.cpp b/STK/Marimba.cpp new file mode 100644 index 0000000..8a9423b --- /dev/null +++ b/STK/Marimba.cpp @@ -0,0 +1,116 @@ +/*******************************************/ +/* Marimba SubClass of Modal4 Instrument, */ +/* by Perry R. Cook, 1995-96 */ +/* */ +/* Controls: CONTROL1 = stickHardness */ +/* CONTROL2 = strikePosition*/ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ +/*******************************************/ + +#include "Marimba.h" +#include "SKINI11.msg" + +Marimba :: Marimba() : Modal4() +{ + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + wave = new RawWvIn(strcat(file,"rawwaves/marmstk1.raw"),"oneshot"); + wave->normalize(); + wave->setRate((MY_FLOAT) 0.5); /* normal stick */ + this->setRatioAndReson(0, (MY_FLOAT) 1.00,(MY_FLOAT) 0.9996); /* Set all 132.0 */ + this->setRatioAndReson(1, (MY_FLOAT) 3.99,(MY_FLOAT) 0.9994); /* of our 523.0 */ + this->setRatioAndReson(2,(MY_FLOAT) 10.65,(MY_FLOAT) 0.9994); /* default 1405.0 */ + this->setRatioAndReson(3,-(MY_FLOAT) 2443.0,(MY_FLOAT) 0.999); /* resonances 2443.0 */ + this->setFiltGain(0,(MY_FLOAT) 0.04); /* and */ + this->setFiltGain(1,(MY_FLOAT) 0.01); /* gains */ + this->setFiltGain(2,(MY_FLOAT) 0.01); /* for each */ + this->setFiltGain(3,(MY_FLOAT) 0.008); /* resonance */ + directGain = (MY_FLOAT) 0.1; + multiStrike = 0; +} + +Marimba :: ~Marimba() +{ + delete wave; +} + +void Marimba :: setStickHardness(MY_FLOAT hardness) +{ + stickHardness = hardness; + wave->setRate((MY_FLOAT) (0.25 * (MY_FLOAT) pow(4.0,stickHardness))); + masterGain = (MY_FLOAT) 0.1 + ((MY_FLOAT) 1.8 * stickHardness); +} + +void Marimba :: setStrikePosition(MY_FLOAT position) +{ + MY_FLOAT temp,temp2; + temp2 = position * PI; + strikePosition = position; /* Hack only first three modes */ + temp = (MY_FLOAT) sin(temp2); + this->setFiltGain(0,(MY_FLOAT) 0.12 * temp); /* 1st mode function of pos. */ + temp = (MY_FLOAT) sin(0.05 + (3.9 * temp2)); + this->setFiltGain(1,(MY_FLOAT) -0.03 * temp); /* 2nd mode function of pos. */ + temp = (MY_FLOAT) sin(-0.05 + (11 * temp2)); + this->setFiltGain(2,(MY_FLOAT) 0.11 * temp); /* 3rd mode function of pos. */ +} + +void Marimba :: setModulationSpeed(MY_FLOAT mSpeed) +{ + /* don't bother here, marimba decay so fast, mod doesn't make sense */ +} + +void Marimba :: setModulationDepth(MY_FLOAT mDepth) +{ +} + +void Marimba :: strike(MY_FLOAT amplitude) +{ + int temp; + temp = rand() >> 10; + if (temp < 2) { + multiStrike = 1; +#if defined(_debug_) + printf("striking twice here!!\n"); +#endif + } + else if (temp < 1) { + multiStrike = 2; +#if defined(_debug_) + printf("striking three times here!!!\n"); +#endif + } +else multiStrike = 0; +Modal4::strike(amplitude); +} + +void Marimba :: controlChange(int number, MY_FLOAT value) +{ +#if defined(_debug_) + printf("Marimba : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_StickHardness_) + this->setStickHardness(value * NORM_7); + else if (number == __SK_StrikePosition_) + this->setStrikePosition(value * NORM_7); + else if (number == __SK_ModFrequency_) + vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0)); + else if (number == __SK_ModWheel_) + vibrGain = (value * NORM_7); + else if (number == __SK_AfterTouch_Cont_) + this->strike(value * NORM_7); + else { + printf("Marimba : Undefined Control Number!!\n"); + } +} + +MY_FLOAT Marimba :: tick() +{ + if (multiStrike>0) + if (wave->isFinished()) { + wave->reset(); + multiStrike -= 1; + } + return Modal4::tick(); +} diff --git a/Marimba.h b/STK/Marimba.h similarity index 100% rename from Marimba.h rename to STK/Marimba.h diff --git a/STK/MatWvIn.cpp b/STK/MatWvIn.cpp new file mode 100644 index 0000000..b716115 --- /dev/null +++ b/STK/MatWvIn.cpp @@ -0,0 +1,178 @@ +/*******************************************/ +/* MatWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to open Matlab MAT-file data */ +/* (doubles) files for playback. In */ +/* order for this class to work, the */ +/* MAT-file must contain a single array */ +/* (matrix) of double-precision floating */ +/* point values (can be multi-channel). */ +/* It does not work for any other data */ +/* formats. */ +/* */ +/* MAT-file data is either big- or */ +/* little-endian, which can be determined */ +/* from the header. */ +/*******************************************/ + +#include "MatWvIn.h" +#include "swapstuf.h" + +MatWvIn :: MatWvIn(char *fileName, char *mode) +{ + extern double SwapDouble(double); + + // Open the file and get header info + FILE *fd; + fd = fopen(fileName,"rb"); + if (!fd) { + printf("Couldn't open or find MAT-file %s !!!\n",fileName); + exit(0); + } + + // Make sure this is a version 5 MAT-file format and find its endian-ness + char head[4]; + fseek(fd,0,SEEK_SET); + fread(&head,4,1,fd); // If any of the first 4 characters of the header = 0, + if (strstr(head,"0")) { // then this is a Version 4 MAT-file. + printf("This looks like a Version 4 MAT-file. I don't support\n"); + printf("that at the moment, but you can add the code to MatWvIn if\n"); + printf("you really need it.\n"); + exit(0); + } + char mi[2]; + int swap = 0; + fseek(fd,126,SEEK_SET); // Locate "M" and "I" characters in header + fread(&mi,2,1,fd); +#ifdef __LITTLE_ENDIAN__ + if (!strncmp(mi,"MI",2)) { + swap = 1; + } else if (strncmp(mi,"IM",2)) { + printf("This doesn't appear to be a MAT-file %s !!!\n",fileName); + exit(0); + } +#else + if (!strncmp(mi,"IM",2)) { + swap = 1; + } else if (strncmp(mi,"MI",2)) { + printf("This doesn't appear to be a MAT-file %s !!!\n",fileName); + exit(0); + } +#endif + + // Check the data element type + INT32 datatype; + fread(&datatype,4,1,fd); + if (swap) datatype = SwapINT32(datatype); + if (datatype != 14) { + printf("I'm expecting a single array (or matrix) data element.\n"); + printf("This doesn't appear to be the case with your data. Sorry!\n"); + exit(0); + } + + // Check the array data type + INT32 tmp; + INT32 size; + fseek(fd,168,SEEK_SET); + fread(&tmp,4,1,fd); + if (swap) tmp = SwapINT32(tmp); + if (tmp == 1) { // array name > 4 characters + fread(&tmp,4,1,fd); // get array name length + if (swap) tmp = SwapINT32(tmp); + size = (INT32) ceil((float)tmp/8); + fseek(fd,size*8,SEEK_CUR); // jump over array name + } + else { // array name <= 4 characters, compressed data element + fseek(fd,4,SEEK_CUR); + } + fread(&tmp,4,1,fd); + if (swap) tmp = SwapINT32(tmp); + if (tmp != 9) { + printf("I'm expecting the array data to be in double precision\n"); + printf("floating-point format. This doesn't appear to be the case\n"); + printf("with your data. Sorry!\n"); + exit(0); + } + + // Get number of rows from the header + INT32 rows; + fseek(fd,160,SEEK_SET); + fread(&rows,4,1,fd); // rows + if (swap) rows = SwapINT32(rows); + + // Get number of columns from the header + INT32 columns; + fread(&columns,4,1,fd); // columns + if (swap) columns = SwapINT32(columns); + + // Make channels = smaller of rows or columns + if (rows < columns) { + channels = rows; + length = columns; + } + else { + channels = columns; + length = rows; + } + data = (MY_FLOAT *) new MY_FLOAT[(length+1)*channels]; + + // Move read pointer to the data in the file + INT32 headsize; + fseek(fd,132,SEEK_SET); + fread(&headsize,4,1,fd); // file size from 132nd byte + if (swap) headsize = SwapINT32(headsize); + headsize -= length * 8 * channels; + fseek(fd,headsize,SEEK_CUR); + + // Read samples into data[] + long i = 0; + double temp; + if (channels == rows) { + while (fread(&temp,8,1,fd)) { + if (swap) temp = SwapDouble(temp); + data[i++] = (MY_FLOAT) temp; + } + } else { + long j = 0; + while (fread(&temp,8,1,fd)) { + if (swap) temp = SwapDouble(temp); + data[channels*i+j] = (MY_FLOAT) temp; + i++; + if (!(i 31) { // Check length of array name + namelen = 31; + printf("File name too long for MATLAB array name.\n"); + printf("Using first 31 characters only.\n"); + } + + strncpy(arrayName,fileName,namelen); + if (namelen > 4) { + hdr.adf[10] = 1; // Matlab 8-bit signed integer data type value + } + else { // Compressed data element format + hdr.adf[10] = namelen; + hdr.adf[10] <<=16; + hdr.adf[10] += 1; + } + headsize = 40; // Number of bytes in data element so far + + // Format file name + strcpy(tempName,fileName); + if (strstr(tempName,".mat")==NULL) strcat(tempName,".mat"); + + // Open file and write fixed header information + fd = fopen(tempName,"w+b"); + if (!fd) { + printf("Couldn't create matfile %s !!!!!!!!\n",fileName); + exit(0); + } + + arrayName[namelen] = '\0'; + printf("\nCreating MAT-file %s with MATLAB array %s\n\n", tempName, arrayName); + fwrite(&hdr,sizeof(char),172,fd); // Write the fixed portion of the header + + // Write MATLAB array name + if (namelen > 4) { + fwrite(&namelen,sizeof(INT32),1,fd); // Size of array name in bytes (chars) + fwrite(arrayName,sizeof(char),namelen,fd); + tmp = (INT32) ceil((float)namelen/8); + fseek(fd,tmp*8-namelen,SEEK_CUR); + headsize += tmp*8; + } + else { // Compressed data element format + fwrite(arrayName,sizeof(char),namelen,fd); + tmp = 4-namelen; + fseek(fd,tmp,SEEK_CUR); + } + + // Finish writing known header information + tmp = 9; // Matlab IEEE 754 double data type + fwrite(&tmp,sizeof(INT32),1,fd); + tmp = 0; // Size of real part subelement in bytes (8 per sample) + fwrite(&tmp,sizeof(INT32),1,fd); + headsize += 8; // Total number of bytes in data element so far + + fseek(fd,132,SEEK_SET); + fwrite(&headsize,sizeof(INT32),1,fd); // Write header size ... will update at end + fseek(fd,0,SEEK_END); + + return fd; +} + +MatWvOut :: MatWvOut(char *fileName) +{ + channels = 1; + fd = openMatFile(channels,fileName); + counter = 0; + totalCount = 0; +} + +MatWvOut :: MatWvOut(int chans, char *fileName) +{ + channels = chans; + fd = openMatFile(channels,fileName); + counter = 0; + totalCount = 0; +} + +MatWvOut :: ~MatWvOut() +{ + double temp; + INT32 headsize, temp1; + + fwrite(data,sizeof(double),counter,fd); + temp = (double) totalCount * ONE_OVER_SRATE; + printf("%f Seconds Computed\n",temp); + fseek(fd,164,SEEK_SET); + fwrite(&totalCount,sizeof(INT32),1,fd); /* Write number of columns */ + fseek(fd,132,SEEK_SET); + fread(&headsize,sizeof(INT32),1,fd); + temp1 = headsize; + headsize += (INT32) (totalCount * 8 * channels); + fseek(fd,132,SEEK_SET); + fwrite(&headsize,sizeof(INT32),1,fd); /* Write file size (minus some header info) */ + fseek(fd,temp1+132,SEEK_SET); + temp1 = totalCount * 8 * channels; + fwrite(&temp1,sizeof(INT32),1,fd); /* Write data size (in bytes) */ + fclose(fd); +} + +INT32 MatWvOut :: getCounter() +{ + return totalCount; +} + +MY_FLOAT MatWvOut :: getTime() +{ + return (MY_FLOAT) totalCount * ONE_OVER_SRATE; +} + +void MatWvOut :: tick(MY_FLOAT sample) +{ + for (int i=0;inormalize(); - vibr->setFreq((MY_FLOAT) 6.0); - vibrGain = (MY_FLOAT) 0.05; + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); + vibr->normalize(); + vibr->setFreq((MY_FLOAT) 6.0); + vibrGain = (MY_FLOAT) 0.05; - directGain = (MY_FLOAT) 0.0; - masterGain = (MY_FLOAT) 1.0; - baseFreq = (MY_FLOAT) 440.0; - this->setRatioAndReson(0,(MY_FLOAT) 1.00,(MY_FLOAT) 0.9997); /* Set some */ - this->setRatioAndReson(1,(MY_FLOAT) 1.30,(MY_FLOAT) 0.9997); /* silly */ - this->setRatioAndReson(2,(MY_FLOAT) 1.77,(MY_FLOAT) 0.9997); /* default */ - this->setRatioAndReson(3,(MY_FLOAT) 2.37,(MY_FLOAT) 0.9997); /* values here */ - this->setFiltGain(0,(MY_FLOAT) 0.01); - this->setFiltGain(1,(MY_FLOAT) 0.01); - this->setFiltGain(2,(MY_FLOAT) 0.01); - this->setFiltGain(3,(MY_FLOAT) 0.01); - this->clear(); - filters[0]->setEqualGainZeroes(); - filters[1]->setEqualGainZeroes(); - filters[2]->setEqualGainZeroes(); - filters[3]->setEqualGainZeroes(); - stickHardness = (MY_FLOAT) 0.5; - strikePosition = (MY_FLOAT) 0.561; + directGain = (MY_FLOAT) 0.0; + masterGain = (MY_FLOAT) 1.0; + baseFreq = (MY_FLOAT) 440.0; + this->setRatioAndReson(0,(MY_FLOAT) 1.00,(MY_FLOAT) 0.9997); /* Set some */ + this->setRatioAndReson(1,(MY_FLOAT) 1.30,(MY_FLOAT) 0.9997); /* silly */ + this->setRatioAndReson(2,(MY_FLOAT) 1.77,(MY_FLOAT) 0.9997); /* default */ + this->setRatioAndReson(3,(MY_FLOAT) 2.37,(MY_FLOAT) 0.9997); /* values here */ + this->setFiltGain(0,(MY_FLOAT) 0.01); + this->setFiltGain(1,(MY_FLOAT) 0.01); + this->setFiltGain(2,(MY_FLOAT) 0.01); + this->setFiltGain(3,(MY_FLOAT) 0.01); + this->clear(); + filters[0]->setEqualGainZeroes(); + filters[1]->setEqualGainZeroes(); + filters[2]->setEqualGainZeroes(); + filters[3]->setEqualGainZeroes(); + stickHardness = (MY_FLOAT) 0.5; + strikePosition = (MY_FLOAT) 0.561; } Modal4 :: ~Modal4() diff --git a/Modal4.h b/STK/Modal4.h similarity index 94% rename from Modal4.h rename to STK/Modal4.h index 1737757..2758649 100644 --- a/Modal4.h +++ b/STK/Modal4.h @@ -11,8 +11,7 @@ #include "Instrmnt.h" #include "Envelope.h" -#include "RawInterp.h" -#include "RawLoop.h" +#include "RawWvIn.h" #include "BiQuad.h" #include "OnePole.h" @@ -20,10 +19,10 @@ class Modal4 : public Instrmnt { protected: Envelope *envelope; - RawInterp *wave; + RawWvIn *wave; BiQuad *filters[4]; OnePole *onepole; - RawLoop *vibr; + RawWvIn *vibr; MY_FLOAT vibrGain; MY_FLOAT masterGain; MY_FLOAT directGain; diff --git a/Modulatr.cpp b/STK/Modulatr.cpp similarity index 53% rename from Modulatr.cpp rename to STK/Modulatr.cpp index 9c122df..3303a8c 100644 --- a/Modulatr.cpp +++ b/STK/Modulatr.cpp @@ -12,55 +12,58 @@ Modulatr :: Modulatr() { - vibwave = new RawLoop("rawwaves/sinewave.raw"); - vibwave->normalize(); - vibwave->setFreq((MY_FLOAT) 6.0); - vibAmt = (MY_FLOAT) 0.04; - noise = new SubNoise(330); - rndAmt = (MY_FLOAT) 0.005; - onepole = new OnePole; - onepole->setPole(POLE_POS); - onepole->setGain(rndAmt * RND_SCALE); + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + vibwave = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); + vibwave->normalize(); + vibwave->setFreq((MY_FLOAT) 6.0); + vibAmt = (MY_FLOAT) 0.04; + noise = new SubNoise(330); + rndAmt = (MY_FLOAT) 0.005; + onepole = new OnePole; + onepole->setPole(POLE_POS); + onepole->setGain(rndAmt * RND_SCALE); } Modulatr :: ~Modulatr() { - delete vibwave; - delete noise; - delete onepole; + delete vibwave; + delete noise; + delete onepole; } void Modulatr :: reset() { - lastOutput = (MY_FLOAT) 0.0; + lastOutput = (MY_FLOAT) 0.0; } void Modulatr :: setVibFreq(MY_FLOAT vibFreq) { - vibwave->setFreq(vibFreq); + vibwave->setFreq(vibFreq); } void Modulatr :: setVibAmt(MY_FLOAT vibAmount) { - vibAmt = vibAmount; + vibAmt = vibAmount; } void Modulatr :: setRndAmt(MY_FLOAT rndAmount) { - rndAmt = rndAmount; - onepole->setGain(RND_SCALE * rndAmt); + rndAmt = rndAmount; + onepole->setGain(RND_SCALE * rndAmt); } MY_FLOAT Modulatr :: tick() { - lastOutput = vibAmt * vibwave->tick(); /* Compute periodic and */ - lastOutput += onepole->tick(noise->tick()); /* random modulations */ - return lastOutput; + lastOutput = vibAmt * vibwave->tick(); /* Compute periodic and */ + lastOutput += onepole->tick(noise->tick()); /* random modulations */ + return lastOutput; } MY_FLOAT Modulatr :: lastOut() { - return lastOutput; + return lastOutput; } /************ Test Main Program *****************/ diff --git a/Modulatr.h b/STK/Modulatr.h similarity index 52% rename from Modulatr.h rename to STK/Modulatr.h index 9c4d6d8..f2b3baa 100644 --- a/Modulatr.h +++ b/STK/Modulatr.h @@ -9,28 +9,28 @@ #define __Modulatr_h #include "Object.h" -#include "RawLoop.h" +#include "RawWvIn.h" #include "SubNoise.h" #include "OnePole.h" class Modulatr : public Object { - protected: - RawLoop *vibwave; - SubNoise *noise; - OnePole *onepole; - MY_FLOAT vibAmt; - MY_FLOAT rndAmt; - MY_FLOAT lastOutput; - public: - Modulatr(); - ~Modulatr(); - void reset(); - void setVibFreq(MY_FLOAT vibFreq); - void setVibAmt(MY_FLOAT vibAmount); - void setRndAmt(MY_FLOAT rndAmount); - MY_FLOAT tick(); - MY_FLOAT lastOut(); + protected: + RawWvIn *vibwave; + SubNoise *noise; + OnePole *onepole; + MY_FLOAT vibAmt; + MY_FLOAT rndAmt; + MY_FLOAT lastOutput; + public: + Modulatr(); + ~Modulatr(); + void reset(); + void setVibFreq(MY_FLOAT vibFreq); + void setVibAmt(MY_FLOAT vibAmount); + void setRndAmt(MY_FLOAT rndAmount); + MY_FLOAT tick(); + MY_FLOAT lastOut(); }; #endif diff --git a/STK/Moog1.cpp b/STK/Moog1.cpp new file mode 100644 index 0000000..5cedace --- /dev/null +++ b/STK/Moog1.cpp @@ -0,0 +1,114 @@ +/******************************************/ +/* Test Sampler Subclass of */ +/* Sampling Synthesizer Class */ +/* by Perry R. Cook, 1995-96 */ +/* */ +/* Controls: CONTROL1 = filterQ */ +/* CONTROL2 = filterRate */ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ +/******************************************/ + +#include "Moog1.h" +#include "SKINI11.msg" + +Moog1 :: Moog1() : SamplFlt() +{ + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char temp[128]; + char file[128]; + strcpy(temp, RAWWAVE_PATH); + strcpy(file,temp); + attacks[0] = new RawWvIn(strcat(file,"rawwaves/mandpluk.raw"),"oneshot"); + strcpy(file,temp); + loops[0] = new RawWvIn(strcat(file,"rawwaves/impuls20.raw"),"looping"); + strcpy(file,temp); + loops[1] = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); /* Steal one for vibrato */ + attacks[0]->normalize(); + loops[0]->normalize(); + loops[1]->normalize(); + loops[1]->setFreq((MY_FLOAT) 6.122); + adsr->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.5,(MY_FLOAT) 0.6,(MY_FLOAT) 0.250); + filterQ = (MY_FLOAT) 0.85; + filterRate = (MY_FLOAT) 0.0001; + modDepth = (MY_FLOAT) 0.0; +} + +Moog1 :: ~Moog1() +{ + delete attacks[0]; + delete loops[0]; + delete loops[1]; +} + +void Moog1 :: setFreq(MY_FLOAT frequency) +{ + baseFreq = frequency; + attacks[0]->setFreq(baseFreq * (MY_FLOAT) 0.01); + loops[0]->setFreq(baseFreq); +} + +void Moog1 :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + MY_FLOAT temp; + + this->setFreq(freq); + this->keyOn(); + attackGain = amp * (MY_FLOAT) 0.5; + loopGain = amp; + + temp = filterQ + (MY_FLOAT) 0.05; + filters[0]->setStates((MY_FLOAT) 2000.0,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); + filters[1]->setStates((MY_FLOAT) 2000.0,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); + temp = filterQ + (MY_FLOAT) 0.099; + filters[0]->setTargets((MY_FLOAT) freq,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); + filters[1]->setTargets((MY_FLOAT) freq,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp)); + filters[0]->setSweepRate(filterRate * (MY_FLOAT) 22050.0 / SRATE); + filters[1]->setSweepRate(filterRate * (MY_FLOAT) 22050.0 / SRATE); +#if defined(_debug_) + printf("Moog1 : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + +void Moog1 :: setModulationSpeed(MY_FLOAT mSpeed) +{ + loops[1]->setFreq(mSpeed); +} + +void Moog1 :: setModulationDepth(MY_FLOAT mDepth) +{ + modDepth = mDepth * (MY_FLOAT) 0.5; +} + +void Moog1 :: controlChange(int number, MY_FLOAT value) +{ +#if defined(_debug_) + printf("Moog1 : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_FilterQ_) + filterQ = (MY_FLOAT) 0.80 + ((MY_FLOAT) 0.1 * value * NORM_7); + else if (number == __SK_FilterSweepRate_) + filterRate = (value * NORM_7 * (MY_FLOAT) 0.0002); + else if (number == __SK_ModFrequency_) + this->setModulationSpeed(value * NORM_7 * (MY_FLOAT) 12.0); + else if (number == __SK_ModWheel_) + this->setModulationDepth(value * NORM_7); + else if (number == __SK_AfterTouch_Cont_) + adsr->setTarget(value * NORM_7); + else { + printf("Moog1 : Undefined Control Number!!\n"); + } +} + +MY_FLOAT Moog1 :: tick() +{ + MY_FLOAT temp; + + if (modDepth!=0.0) { + temp = loops[1]->tick() * modDepth; + loops[0]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp)); + } + lastOutput = SamplFlt :: tick(); + return lastOutput; +} + diff --git a/Moog1.h b/STK/Moog1.h similarity index 51% rename from Moog1.h rename to STK/Moog1.h index 018a296..4d5b3e2 100644 --- a/Moog1.h +++ b/STK/Moog1.h @@ -5,8 +5,8 @@ /* */ /* Controls: CONTROL1 = filterQ */ /* CONTROL2 = filterRate */ -/* CONTROL3 = vibFreq */ -/* MOD_WHEEL= vibAmt */ +/* CONTROL3 = vibFreq */ +/* MOD_WHEEL= vibAmt */ /******************************************/ #if !defined(__Moog1_h) @@ -16,19 +16,19 @@ class Moog1 : public SamplFlt { - private: - MY_FLOAT modDepth; - MY_FLOAT filterQ; - MY_FLOAT filterRate; - public: - Moog1(); - ~Moog1(); - virtual void setFreq(MY_FLOAT frequency); - virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); - void setModulationSpeed(MY_FLOAT mSpeed); - void setModulationDepth(MY_FLOAT mDepth); - virtual void controlChange(int number, MY_FLOAT value); - virtual MY_FLOAT tick(); + private: + MY_FLOAT modDepth; + MY_FLOAT filterQ; + MY_FLOAT filterRate; + public: + Moog1(); + ~Moog1(); + virtual void setFreq(MY_FLOAT frequency); + virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); + void setModulationSpeed(MY_FLOAT mSpeed); + void setModulationDepth(MY_FLOAT mDepth); + virtual void controlChange(int number, MY_FLOAT value); + virtual MY_FLOAT tick(); }; #endif diff --git a/NRev.cpp b/STK/NRev.cpp similarity index 100% rename from NRev.cpp rename to STK/NRev.cpp diff --git a/NRev.h b/STK/NRev.h similarity index 100% rename from NRev.h rename to STK/NRev.h diff --git a/Noise.cpp b/STK/Noise.cpp similarity index 87% rename from Noise.cpp rename to STK/Noise.cpp index 51daf3f..d978ec3 100644 --- a/Noise.cpp +++ b/STK/Noise.cpp @@ -5,11 +5,11 @@ /*******************************************/ #include "Noise.h" -#ifdef __NeXT_ +#ifdef __OS_NeXT_ #include #endif -#if defined(__OS_Win_) /* For Windows95 or NT */ +#if defined(__OS_Win_) /* For Windoze */ #define ONE_OVER_RANDLIMIT 0.00006103516 #else /* This is for Linux, NeXT and SGI */ #define ONE_OVER_RANDLIMIT 0.00000000093132258 @@ -26,8 +26,7 @@ Noise :: ~Noise() MY_FLOAT Noise :: tick() { -/* THIS ONE IS WIN95 */ -#if defined(__OS_Win_) /* For Windows95 or NT */ +#if defined(__OS_Win_) /* For Windoze */ lastOutput = (MY_FLOAT) (rand() - 16383); #else /* This is for Linux, NeXT and SGI */ lastOutput = (MY_FLOAT) random() - 1073741823.0; diff --git a/Noise.h b/STK/Noise.h similarity index 100% rename from Noise.h rename to STK/Noise.h diff --git a/Object.cpp b/STK/Object.cpp similarity index 100% rename from Object.cpp rename to STK/Object.cpp diff --git a/STK/Object.h b/STK/Object.h new file mode 100644 index 0000000..6607c1c --- /dev/null +++ b/STK/Object.h @@ -0,0 +1,131 @@ +/*********************************************/ +/* Object Class, by Perry R. Cook, 1995-99 */ +/* */ +/* This is mostly here for compatibility */ +/* with Objective C. We'll also stick */ +/* global defines here, so everyone will */ +/* see them. */ +/*********************************************/ + +#if !defined(__Object_h) +#define __Object_h + +#include +#include +#include +#include + +class Object +{ + public: + protected: + Object(); + virtual ~Object(); +}; + +/* The OS type definitions are made in the Makefile */ + +#if defined(__OS_NeXT_) /* For NeXTStep - Black or White Hardware */ +// No special defines at this time +#elif defined(__OS_IRIX_) /* For SGI */ + #define __STK_REALTIME_ +#elif defined(__OS_Linux_) /* For Linux */ + #define __STK_REALTIME_ + #define __OSS_API_ /* Use OSS API */ + #define __LITTLE_ENDIAN__ +#elif defined(__OS_Win_) /* For WindowsXX or NT */ + #define __STK_REALTIME_ + #define __WINDS_API_ /* For DirectSound API */ +// #define __WINMM_API_ /* For Win MM API */ + #define __LITTLE_ENDIAN__ +#endif + +/* Real-time output buffer size. If clicks are occuring in the + * output sound stream, a larger buffer size may help. Larger + * buffer sizes, however, produce more latency between input and + * output. + * NOTE FOR WINDOZE USERS: Given inherent delays in the sound + * output mechanism under Windoze, there is a trade-off between + * smoothness of fast SKINI parameter updates and input/output + * latency as discussed above. You can use buffer sizes as low + * as 100 (maybe lower) for delay critical applications, but in + * this case SKINI parameter updates will be clumped together + * and sound unnatural. If you want smoother parameter updates + * and you can live with more delay, try using buffer sizes + * closer to 1000. If you need smooth parameter updates AND + * low delay, don't use Windoze! +*/ +#define RT_BUFFER_SIZE 256 + +/* This sets the maximum number of simultaneous + * (within a buffer) MIDI messages that can be + * serviced before messages begin to be lost or + * overwritten. It should be a function of + * RT_BUFFER_SIZE + */ +#define MAX_IN_STRINGS 25 + +/* The following definition is concatenated to the + * beginning of all references to rawwave files in + * the various STK core classes (ex. Clarinet.cpp). + * If you wish to move the rawwaves directory to a + * different location in your file system, you will + * need to set this path definition appropriately. + * The current definition is a relative reference + * that will work "out of the box" for the STK + * distribution. + */ +#define RAWWAVE_PATH "../" + +/* Sampling Rate */ +#define SRATE (MY_FLOAT) 22050.0 + +/* Other SRATE derived defines */ +#define SRATE_OVER_TWO (MY_FLOAT) (SRATE / 2) +#define ONE_OVER_SRATE (MY_FLOAT) (1 / SRATE) +#define TWO_PI_OVER_SRATE (MY_FLOAT) (2 * PI / SRATE) + +/* Yer Basic Trigonometric constants */ +#if !defined(PI) + #define PI (MY_FLOAT) 3.14159265359 +#endif +#define TWO_PI (MY_FLOAT) 6.28318530718 +#define ONE_OVER_TWO_PI (MY_FLOAT) 0.15915494309 +#define SQRT_TWO 1.414213562 + +/* States for Envelopes, etc. */ +#define ATTACK 0 +#define DECAY 1 +#define SUSTAIN 2 +#define RELEASE 3 + +/* Machine dependent stuff, possibly useful for optimization. + * For example, changing double to float here increases + * performance (speed) by a whopping 4-6% on 486-flavor machines. + * BUT!! a change from float to double here increases speed by + * 30% or so on SGI machines. +*/ +#define MY_FLOAT double +//#define MY_FLOAT float + +/* MY_MULTI is just a pointer to MY_FLOAT. This type is used + * to pass multichannel data back and forth within STK. +*/ +typedef MY_FLOAT *MY_MULTI; + +/* INT16 is just that ... a 16-bit signed integer. */ +typedef signed short INT16; + +/* INT32 is just that ... a 32-bit signed integer. */ +typedef signed long INT32; + +/* Debugging define, causes massive printf's to come out. + * Also enables timing calculations in WaveOut class, other stuff. + * Uncomment to enable. + */ +//#define _debug_ 1 + +/* MIDI definitions */ +#define NORM_7 (MY_FLOAT) 0.0078125 /* this is 1/128 */ + +#endif diff --git a/STK/OnePole.cpp b/STK/OnePole.cpp new file mode 100644 index 0000000..fd096d4 --- /dev/null +++ b/STK/OnePole.cpp @@ -0,0 +1,62 @@ +/*******************************************/ +/* One Pole Filter Class, */ +/* by Perry R. Cook, 1995-96 */ +/* The parameter gain is an additional */ +/* gain parameter applied to the filter */ +/* on top of the normalization that takes */ +/* place automatically. So the net max */ +/* gain through the system equals the */ +/* value of gain. sgain is the combina- */ +/* tion of gain and the normalization */ +/* parameter, so if you set the poleCoeff */ +/* to alpha, sgain is always set to */ +/* gain * (1.0 - fabs(alpha)). */ +/*******************************************/ + +#include "OnePole.h" + +OnePole :: OnePole() : Filter() +{ + poleCoeff = (MY_FLOAT) 0.9; + gain = (MY_FLOAT) 1.0; + sgain = (MY_FLOAT) 0.1; + outputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT)); + outputs[0] = (MY_FLOAT) 0.0; +} + +OnePole :: ~OnePole() +{ + free(outputs); +} + +void OnePole :: clear() +{ + outputs[0] = (MY_FLOAT) 0.0; + lastOutput = (MY_FLOAT) 0.0; +} + +void OnePole :: setPole(MY_FLOAT aValue) +{ + poleCoeff = aValue; + if (poleCoeff > 0.0) // Normalize gain to 1.0 max + sgain = gain * ((MY_FLOAT) 1.0 - poleCoeff); + else + sgain = gain * ((MY_FLOAT) 1.0 + poleCoeff); +} + +void OnePole :: setGain(MY_FLOAT aValue) +{ + gain = aValue; + if (poleCoeff > 0.0) + sgain = gain * ((MY_FLOAT) 1.0 - poleCoeff); // Normalize gain to 1.0 max + else + sgain = gain * ((MY_FLOAT) 1.0 + poleCoeff); +} + +MY_FLOAT OnePole :: tick(MY_FLOAT sample) // Perform Filter Operation +{ + outputs[0] = (sgain * sample) + (poleCoeff * outputs[0]); + lastOutput = outputs[0]; + return lastOutput; +} + diff --git a/OnePole.h b/STK/OnePole.h similarity index 100% rename from OnePole.h rename to STK/OnePole.h diff --git a/OneZero.cpp b/STK/OneZero.cpp similarity index 50% rename from OneZero.cpp rename to STK/OneZero.cpp index ff2475e..4ade8d0 100644 --- a/OneZero.cpp +++ b/STK/OneZero.cpp @@ -17,11 +17,11 @@ OneZero :: OneZero() { - gain = (MY_FLOAT) 1.0; - zeroCoeff = (MY_FLOAT) 1.0; - sgain = (MY_FLOAT) 0.5; - inputs = (MY_FLOAT *) malloc(MY_FLOAT_SIZE); - this->clear(); + gain = (MY_FLOAT) 1.0; + zeroCoeff = (MY_FLOAT) 1.0; + sgain = (MY_FLOAT) 0.5; + inputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT)); + this->clear(); } OneZero :: ~OneZero() @@ -31,34 +31,34 @@ OneZero :: ~OneZero() void OneZero :: clear() { - inputs[0] = (MY_FLOAT) 0.0; - lastOutput = (MY_FLOAT) 0.0; + inputs[0] = (MY_FLOAT) 0.0; + lastOutput = (MY_FLOAT) 0.0; } void OneZero :: setGain(MY_FLOAT aValue) { - gain = aValue; - if (zeroCoeff > 0.0) /* Normalize gain to 1.0 max */ - sgain = gain / ((MY_FLOAT) 1.0 + zeroCoeff); - else - sgain = gain / ((MY_FLOAT) 1.0 - zeroCoeff); + gain = aValue; + if (zeroCoeff > 0.0) // Normalize gain to 1.0 max + sgain = gain / ((MY_FLOAT) 1.0 + zeroCoeff); + else + sgain = gain / ((MY_FLOAT) 1.0 - zeroCoeff); } void OneZero :: setCoeff(MY_FLOAT aValue) { - zeroCoeff = aValue; - if (zeroCoeff > 0.0) /* Normalize gain to 1.0 max */ - sgain = gain / ((MY_FLOAT) 1.0 + zeroCoeff); - else - sgain = gain / ((MY_FLOAT) 1.0 - zeroCoeff); + zeroCoeff = aValue; + if (zeroCoeff > 0.0) // Normalize gain to 1.0 max + sgain = gain / ((MY_FLOAT) 1.0 + zeroCoeff); + else + sgain = gain / ((MY_FLOAT) 1.0 - zeroCoeff); } -MY_FLOAT OneZero :: tick(MY_FLOAT sample) /* Perform Filter Operation */ +MY_FLOAT OneZero :: tick(MY_FLOAT sample) // Perform Filter Operation { - MY_FLOAT temp; - temp = sgain * sample; - lastOutput = (inputs[0] * zeroCoeff) + temp; - inputs[0] = temp; - return lastOutput; + MY_FLOAT temp; + temp = sgain * sample; + lastOutput = (inputs[0] * zeroCoeff) + temp; + inputs[0] = temp; + return lastOutput; } diff --git a/OneZero.h b/STK/OneZero.h similarity index 100% rename from OneZero.h rename to STK/OneZero.h diff --git a/PRCRev.cpp b/STK/PRCRev.cpp similarity index 100% rename from PRCRev.cpp rename to STK/PRCRev.cpp diff --git a/PRCRev.h b/STK/PRCRev.h similarity index 100% rename from PRCRev.h rename to STK/PRCRev.h diff --git a/STK/PercFlut.cpp b/STK/PercFlut.cpp new file mode 100644 index 0000000..3334f0b --- /dev/null +++ b/STK/PercFlut.cpp @@ -0,0 +1,63 @@ +/******************************************/ +/* Percussive Flute Subclass */ +/* of Algorithm 4 (TX81Z) Subclass of */ +/* 4 Operator FM Synth */ +/* by Perry R. Cook, 1995-96 */ +/******************************************/ + +#include "PercFlut.h" + +PercFlut :: PercFlut() : FM4Alg4() +{ + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file1[128]; + char file2[128]; + char file3[128]; + char file4[128]; + strcpy(file1, RAWWAVE_PATH); + strcpy(file2, RAWWAVE_PATH); + strcpy(file3, RAWWAVE_PATH); + strcpy(file4, RAWWAVE_PATH); + this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"), + strcat(file2,"rawwaves/sinewave.raw"), + strcat(file3,"rawwaves/sinewave.raw"), + strcat(file4,"rawwaves/fwavblnk.raw")); + + this->setRatio(0,(MY_FLOAT) (1.50 * 1.000)); + this->setRatio(1,(MY_FLOAT) (3.00 * 0.995)); + this->setRatio(2,(MY_FLOAT) (2.99 * 1.005)); + this->setRatio(3,(MY_FLOAT) (6.00 * 0.997)); + gains[0] = __FM4Op_gains[99]; + gains[1] = __FM4Op_gains[71]; + gains[2] = __FM4Op_gains[93]; + gains[3] = __FM4Op_gains[85]; + adsr[0]->setAllTimes((MY_FLOAT) 0.05,(MY_FLOAT) 0.05, + __FM4Op_susLevels[14],(MY_FLOAT) 0.05); + adsr[1]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.50, + __FM4Op_susLevels[13],(MY_FLOAT) 0.5); + adsr[2]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.30, + __FM4Op_susLevels[11],(MY_FLOAT) 0.05); + adsr[3]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.05, + __FM4Op_susLevels[13],(MY_FLOAT) 0.01); + twozero->setGain((MY_FLOAT) 0.0); + modDepth = (MY_FLOAT) 0.005; +} + +void PercFlut :: setFreq(MY_FLOAT frequency) +{ + baseFreq = frequency; +} + +void PercFlut :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + gains[0] = amp * __FM4Op_gains[99] * 0.5; + gains[1] = amp * __FM4Op_gains[71] * 0.5; + gains[2] = amp * __FM4Op_gains[93] * 0.5; + gains[3] = amp * __FM4Op_gains[85] * 0.5; + this->setFreq(freq); + this->keyOn(); +#if defined(_debug_) + printf("PercFlut : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + diff --git a/PercFlut.h b/STK/PercFlut.h similarity index 100% rename from PercFlut.h rename to STK/PercFlut.h diff --git a/Plucked.cpp b/STK/Plucked.cpp similarity index 100% rename from Plucked.cpp rename to STK/Plucked.cpp diff --git a/Plucked.h b/STK/Plucked.h similarity index 100% rename from Plucked.h rename to STK/Plucked.h diff --git a/Plucked2.cpp b/STK/Plucked2.cpp similarity index 100% rename from Plucked2.cpp rename to STK/Plucked2.cpp diff --git a/Plucked2.h b/STK/Plucked2.h similarity index 100% rename from Plucked2.h rename to STK/Plucked2.h diff --git a/STK/PoleZero.cpp b/STK/PoleZero.cpp new file mode 100644 index 0000000..2b9d70a --- /dev/null +++ b/STK/PoleZero.cpp @@ -0,0 +1,63 @@ +/*******************************************/ +/* PoleZero (1-pole, 1-zero) Filter Class */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* See books on filters to understand */ +/* more about how this works. Nothing */ +/* out of the ordinary in this version. */ +/*******************************************/ + +#include "PoleZero.h" + +PoleZero :: PoleZero() : Filter() +{ + inputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT)); + zeroCoeff = (MY_FLOAT) 0.0; + poleCoeff = (MY_FLOAT) 0.0; + gain = (MY_FLOAT) 1.0; + this->clear(); +} + +PoleZero :: ~PoleZero() +{ + free(inputs); +} + +void PoleZero :: clear() +{ + inputs[0] = (MY_FLOAT) 0.0; + lastOutput = (MY_FLOAT) 0.0; +} + +void PoleZero :: setPoleCoeff(MY_FLOAT coeff) +{ + poleCoeff = coeff; +} + +void PoleZero :: setZeroCoeff(MY_FLOAT coeff) +{ + zeroCoeff = coeff; +} + +void PoleZero :: setGain(MY_FLOAT aValue) +{ + gain = aValue; +} + +// PoleZero is one pole, one zero filter +// Look it up in your favorite DSP text +MY_FLOAT PoleZero :: tick(MY_FLOAT sample) +{ + MY_FLOAT temp; + + // Direct Form II Implementation - only 1 state variable + temp = sample * gain; + temp += inputs[0] * poleCoeff; + + lastOutput = temp; + lastOutput += (inputs[0] * zeroCoeff); + inputs[0] = temp; + + return lastOutput; +} + diff --git a/STK/PoleZero.h b/STK/PoleZero.h new file mode 100644 index 0000000..1f273db --- /dev/null +++ b/STK/PoleZero.h @@ -0,0 +1,30 @@ +/*******************************************/ +/* PoleZero (1-pole, 1-zero) Filter Class */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* See books on filters to understand */ +/* more about how this works. Nothing */ +/* out of the ordinary in this version. */ +/*******************************************/ + +#if !defined(__PoleZero_h) +#define __PoleZero_h + +#include "Filter.h" + +class PoleZero : public Filter +{ + protected: + MY_FLOAT poleCoeff; + MY_FLOAT zeroCoeff; + public: + PoleZero(); + ~PoleZero(); + void clear(); + void setPoleCoeff(MY_FLOAT coeff); + void setZeroCoeff(MY_FLOAT coeff); + void setGain(MY_FLOAT aValue); + MY_FLOAT tick(MY_FLOAT sample); +}; + +#endif diff --git a/STK/RTDuplex.cpp b/STK/RTDuplex.cpp new file mode 100644 index 0000000..a0dcd41 --- /dev/null +++ b/STK/RTDuplex.cpp @@ -0,0 +1,97 @@ +/*******************************************/ +/* Real-Time Duplex Input/Output Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object opens the sound i/o */ +/* device, reads buffers in from it, and */ +/* pokes buffers of samples out to it. */ +/* */ +/* At the moment, duplex mode is possible */ +/* only on Linux (OSS), IRIX, and */ +/* Windows95/98 platforms. */ +/*******************************************/ + +#include "RTDuplex.h" + +#if (defined(__STK_REALTIME_) ) + +RTDuplex :: RTDuplex(MY_FLOAT srate, int chans) +{ + // We'll let RTSoundIO deal with channel and srate limitations. + channels = chans; + soundIO = new RTSoundIO(SRATE, channels, "duplex"); + writeCounter = 0; + gain = 0.00003052; + insamples = new MY_FLOAT[channels]; +#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_)) + // This is necessary under IRIX because it scales the input by 0.5 + // when using single-channel input. + if (channels == 1) gain *= 2; +#endif + // Read half a buffer to get started + readCounter = RT_BUFFER_SIZE / 2; + soundIO->recordBuffer(&indata[readCounter],(RT_BUFFER_SIZE/2)); +} + +RTDuplex :: ~RTDuplex() +{ + soundIO->playBuffer(outdata,writeCounter); + writeCounter = 0; + while (writeCounterplayBuffer(outdata,writeCounter); + soundIO->playBuffer(outdata,writeCounter); // Are these extra writes necessary? + soundIO->playBuffer(outdata,writeCounter); + delete soundIO; + delete [ ] insamples; +} + +MY_FLOAT RTDuplex :: tick(MY_FLOAT outsample) +{ + // We offset the data read and data write calls by RT_BUFFER_SIZE / 2 + if (readCounter >= RT_BUFFER_SIZE) { + soundIO->recordBuffer(indata,RT_BUFFER_SIZE); + readCounter = 0; + } + *insamples = (MY_FLOAT) indata[readCounter++]; + if (channels > 1) { + int i; + for (i=1;i= RT_BUFFER_SIZE) { + soundIO->playBuffer(outdata,writeCounter); + writeCounter = 0; + } + return *insamples; +} + +MY_MULTI RTDuplex :: mtick(MY_MULTI outsamples) +{ + int i; + // We offset the data read and data write calls by RT_BUFFER_SIZE / 2 + if (readCounter >= RT_BUFFER_SIZE) { + soundIO->recordBuffer(indata,RT_BUFFER_SIZE); + readCounter = 0; + } + for (i=0;i= RT_BUFFER_SIZE) { + soundIO->playBuffer(outdata,writeCounter); + writeCounter = 0; + } + return insamples; +} + +#endif diff --git a/STK/RTDuplex.h b/STK/RTDuplex.h new file mode 100644 index 0000000..16f804f --- /dev/null +++ b/STK/RTDuplex.h @@ -0,0 +1,38 @@ +/*******************************************/ +/* Real-Time Duplex Input/Output Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object opens the sound i/o */ +/* device, reads buffers in from it, and */ +/* pokes buffers of samples out to it. */ +/* */ +/* At the moment, duplex mode is possible */ +/* only on Linux (OSS), IRIX, and */ +/* Windows95/98 platforms. */ +/*******************************************/ + +#if !defined(__RTDuplex_h) +#define __RTDuplex_h + +#include "Object.h" +#include "RTSoundIO.h" + +class RTDuplex : public Object +{ + protected: + RTSoundIO *soundIO; + short indata[RT_BUFFER_SIZE]; + short outdata[RT_BUFFER_SIZE]; + long readCounter; + long writeCounter; + int channels; + MY_FLOAT gain; + MY_FLOAT *insamples; + public: + RTDuplex(MY_FLOAT srate, int chans); + ~RTDuplex(); + MY_FLOAT tick(MY_FLOAT outsample); + MY_MULTI mtick(MY_MULTI outsamples); +}; + +#endif // defined(__RTDuplex_h) diff --git a/STK/RTSoundIO.cpp b/STK/RTSoundIO.cpp new file mode 100644 index 0000000..d2b7e0e --- /dev/null +++ b/STK/RTSoundIO.cpp @@ -0,0 +1,963 @@ +/******************************************/ +/* RTSoundIO.cpp */ +/* Realtime Sound I/O Object for STK, */ +/* by Gary P. Scavone, 1998-1999. */ +/* */ +/* The sound output sections of this */ +/* object were based in part on code */ +/* by Doug Scott (SGI), Tim Stilson */ +/* (Linux), Bill Putnam (Win Wav), and */ +/* R. Marsanyi (DirectSound). */ +/* */ +/* This object provides a standard API */ +/* across all platforms for STK realtime */ +/* audio input/output. The sound output */ +/* code is fairly robust. Audio input, */ +/* however, is more dependent on the */ +/* capabilities of the particular OS and */ +/* the soundcard being used. For the */ +/* moment, I'll try to provide 1 and 2 */ +/* channel support. */ +/* */ +/* 16-bit integer audio input/output */ +/* data is being assumed. */ +/******************************************/ + +#include "RTSoundIO.h" + +#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_)) + +#include +#include +#include + +RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels, char *mode) +{ + ALconfig audio_port_config; + int lookaheadbuffers = 8; // number of lookahead buffers + long pvbuf[4]; + int nbuf, totalBufSize; + + /* Check the number of channels */ + if (channels > 2) { + fprintf(stderr,"RTSoundIO: Unsupported # of audio i/o channels: %d\n", channels); + exit(0); + } + + /* Create ALconfig structure */ + audio_port_config = ALnewconfig(); + if (!audio_port_config) { + fprintf(stderr,"Couldn't create ALconfig:%s\n", alGetErrorString(oserror())); + exit(0); + } + + /* Configure channels */ + if(ALsetchannels(audio_port_config, channels) < 0) { + fprintf(stderr,"Cannot configure channels: %s\n", alGetErrorString(oserror())); + exit(0); + } + + /* Size the output queue */ + nbuf = (channels == 2) ? lookaheadbuffers : lookaheadbuffers/2; + totalBufSize = RT_BUFFER_SIZE * nbuf; + if(ALsetqueuesize(audio_port_config, totalBufSize) < 0) { + fprintf(stderr,"Cannot configure output queue size: %s\n", alGetErrorString(oserror())); + exit(0); + } + + if (!strcmp(mode,"play")) { // playback only + + /* Open the output audio port */ + audio_port_out = ALopenport("STK output port", "w", audio_port_config); + if(!audio_port_out) { + fprintf(stderr,"Cannot initialize output audio port: %s\n", alGetErrorString(oserror())); + exit(0); + } + + /* Set sample rate parameters */ + pvbuf[0] = AL_OUTPUT_RATE; + pvbuf[1] = (long) srate; + ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2); /* set output SR */ + + /* Tell port to accept refill at buffers - 1 */ + ALsetfillpoint(audio_port_out,RT_BUFFER_SIZE * (lookaheadbuffers - 1)); + audio_port_in = 0; + } + else if (!strcmp(mode,"record")) { // record only + + /* Open the input audio port */ + audio_port_in = ALopenport("STK input port", "r", audio_port_config); + if(!audio_port_in) { + fprintf(stderr,"Cannot initialize input audio port: %s\n", alGetErrorString(oserror())); + exit(0); + } + + /* Set sample rate parameters */ + pvbuf[0] = AL_OUTPUT_RATE; + pvbuf[1] = (long) srate; + ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2); /* set input SR */ + + /* tell port to accept refill at buffers - 1 */ + ALsetfillpoint(audio_port_in,RT_BUFFER_SIZE * (lookaheadbuffers - 1)); + audio_port_out = 0; + } + else if (!strcmp(mode,"duplex")) { // duplex mode + + /* Open the output audio port */ + audio_port_out = ALopenport("STK output port", "w", audio_port_config); + if(!audio_port_out) { + fprintf(stderr,"Cannot initialize output audio port: %s\n", alGetErrorString(oserror())); + exit(0); + } + + /* Open the input audio port */ + audio_port_in = ALopenport("STK input port", "r", audio_port_config); + if(!audio_port_in) { + fprintf(stderr,"Cannot initialize input audio port: %s\n", alGetErrorString(oserror())); + exit(0); + } + + /* Set sample rate parameters */ + pvbuf[0] = AL_OUTPUT_RATE; + pvbuf[1] = (long) srate; + pvbuf[2] = AL_INPUT_RATE; + pvbuf[3] = (long) srate; + ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 4); /* set output SR */ + /* tell port to accept refill at buffers - 1 */ + ALsetfillpoint(audio_port_out,RT_BUFFER_SIZE * (lookaheadbuffers - 1)); + ALsetfillpoint(audio_port_in,RT_BUFFER_SIZE * (lookaheadbuffers - 1)); + } + else { + fprintf(stderr,"Unsupported RTSoundIO mode: %s\n",mode); + exit(0); + } + + ALfreeconfig(audio_port_config); + audio_port_config = 0; +} + +RTSoundIO :: ~RTSoundIO() +{ + if(audio_port_out) ALcloseport(audio_port_out); + audio_port_out=0; + + if(audio_port_in) ALcloseport(audio_port_in); + audio_port_in=0; +} + +int RTSoundIO :: playBuffer(short *buf, int bufsize) +{ + ALwritesamps(audio_port_out, buf, bufsize); + return 0; +} + +int RTSoundIO :: recordBuffer(short *buf, int bufsize) +{ + ALreadsamps(audio_port_in, buf, bufsize); + return 0; +} + + +/* Linux OSS Sound API here */ + +#elif (defined(__STK_REALTIME_) && defined(__OSS_API_)) + +#define ABS(x) ((x < 0) ? (-x) : (x)) + +#include +#include +#include +#include +#include + +RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels, char *mode) +{ + int lookaheadbuffers = 8; // number of lookahead buffers + int nbuf; + + char DEVICE_NAME[100]; + int format; + int stereo; /* 0=mono, 1=stereo */ + int stereoset; + int speed; + int BUFFER_SIZE_LOG; + int fragsize; + + BUFFER_SIZE_LOG = (int)(log10((double)RT_BUFFER_SIZE)/log10(2.0)); + + /* Check the number of channels */ + if (channels > 2) { + fprintf(stderr,"RTSoundIO: Unsupported # of audio i/o channels: %d\n", channels); + exit(0); + } + + if (channels == 2) stereo = 1; + else stereo = 0; + + strcpy(DEVICE_NAME,"/dev/dsp"); + + if (!strcmp(mode,"play")) { // playback only + if ((audio_fd = open(DEVICE_NAME, O_WRONLY, 0)) == -1) + { /* Opening device failed */ + fprintf(stderr,"Cannot open audio device: %s\n",DEVICE_NAME); + exit(0); + } + } + else if (!strcmp(mode,"record")) { // record only + if ((audio_fd = open(DEVICE_NAME, O_RDONLY, 0)) == -1) + { /* Opening device failed */ + fprintf(stderr,"Cannot open audio device: %s\n",DEVICE_NAME); + exit(0); + } + } + else if (!strcmp(mode,"duplex")) { // duplex mode + if ((audio_fd = open(DEVICE_NAME, O_RDWR, 0)) == -1) + { /* Opening device failed */ + fprintf(stderr,"Cannot open audio device: %s\n",DEVICE_NAME); + exit(0); + } + int caps; + if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)) + { + close(audio_fd); + fprintf(stderr,"Error getting device capabilities: %s\n",DEVICE_NAME); + exit(0); + } + if (!(caps & DSP_CAP_DUPLEX)) + { + close(audio_fd); + fprintf(stderr,"Audio device does not support duplex mode: %s\n",DEVICE_NAME); + exit(0); + } + if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0)) + { + close(audio_fd); + fprintf(stderr,"Error setting duplex mode: %s\n",DEVICE_NAME); + exit(0); + } + int cursrc, srcbit = SOUND_MASK_MIC; + ioctl(audio_fd, MIXER_READ(SOUND_MIXER_RECSRC),&cursrc); + srcbit = (srcbit & cursrc); + ioctl(audio_fd,MIXER_WRITE(SOUND_MIXER_RECSRC),&srcbit); + + // The following opens a direct analog line from the mic to the output + //srcbit = 99; + //ioctl(audio_fd,MIXER_WRITE(SOUND_MIXER_IMIX),&srcbit); + } + else { + fprintf(stderr,"Unsupported RTSoundIO mode: %s\n",mode); + exit(0); + } + + /* Size the output queue */ + nbuf = (channels == 2) ? lookaheadbuffers : lookaheadbuffers/2; + + fragsize = (nbuf << 16) + BUFFER_SIZE_LOG; + if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &fragsize)) + { + close(audio_fd); + fprintf(stderr,"Error setting audio buffer size!\n"); + exit(0); + } + + format = AFMT_S16_LE; + if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1) + { /* Fatal error */ + close(audio_fd); + fprintf(stderr,"SNDCTL_DSP_SETFMT error\n"); + exit(0); + } + + if (format != AFMT_S16_LE) + { + close(audio_fd); + fprintf(stderr,"Audio device doesn't support 16-bit signed LE format\n"); + exit(0); + } + + stereoset = stereo; + if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereoset)==-1) + { /* Fatal error */ + close(audio_fd); + fprintf(stderr,"SNDCTL_DSP_STEREO\n"); + exit(0); + } + + if (stereoset != stereo) + { + close(audio_fd); + fprintf(stderr,"The audio device did not set correct stereo mode: %s\n",DEVICE_NAME); + exit(0); + } + + speed = (int)srate; + if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) + { /* Fatal error */ + close(audio_fd); + fprintf(stderr,"SNDCTL_DSP_SPEED\n"); + exit(0); + } + + if (ABS(speed - srate)>100) + { + close(audio_fd); + fprintf(stderr,"The device doesn't support the requested speed.\n"); + exit(0); + } +} + +RTSoundIO :: ~RTSoundIO() +{ + if(audio_fd) close(audio_fd); + audio_fd=0; +} + +int RTSoundIO :: playBuffer(short *buf, int bufsize) +{ + /* The OSS write() routine takes the buffer size in bytes, thus the + multiplication by two. + */ + int len; + + if ((len = write(audio_fd, buf, 2*bufsize)) == -1) + { + fprintf(stderr,"Audio write error!\n"); + return -1; + } + return 0; +} + +int RTSoundIO :: recordBuffer(short *buf, int bufsize) +{ + /* The OSS read() routine takes the buffer size in bytes, thus the + multiplication by two. + */ + int len; + + if ((len = read(audio_fd, buf, 2*bufsize)) == -1) + { + fprintf(stderr,"Audio read error!\n"); + return -1; + } + return 0; +} + + +#elif (defined(__STK_REALTIME_) && defined(__WINDS_API_) ) +/* + * AUDIO OUTPUT: + * + * There are two ways (or more) to go about handling sound output + * under DirectSound. For minimum latency, one should write new + * buffers in front of the read pointer (Method 1). The other + * method is to always write new buffers of data behind the read + * pointer (Method 2). Method 2 is very safe but inherently + * produces a delay equivalent to the entire sound buffer (plus + * any other delays that Microsloth provides). Even using + * Method 1, however, Microsloth requires that you leave about + * 15 ms of buffer between the read and write pointers. I've tried + * both methods and neither results in performance that can compare + * to either Linux or SGI sound output. In order to minimize + * latency, however, I'll go with Method 1 and leave Method 2 + * commented out below. + * + * If the primary sound buffer exists in hardware, I will write + * directly to it. If the primary sound buffer is emulated in + * software, this is not possible and we must use a secondary buffer. + * + * AUDIO INPUT: + * + * I didn't spend a lot of time doing the audio input code. I + * basically got it working, heard that it had noticeable delay, + * but didn't try screwing around to minimize that delay. I provide + * a single variable (at the beginning of the Capture initialization) + * which controls the size of the DirectSound Capture buffer, which + * in turn controls the latency. + * + * The DirectSoundCapture API is only available with DirectX versions + * 5.0 and higher. If you don't have such capabilities (ex. WinNT), + * then use the WinMM API code (uncomment the __WINMM_API_ define + * statement in Object.h and comment out the __WINDS_API_ flag). This + * will allow you to still compile STK, but without the audio input + * capabilities. + */ + +#include + +RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels, char *mode) +{ + HRESULT result; + WAVEFORMATEX wfFormat; + BYTE *pAudioPtr; + DWORD dwDataLen; + + // Initialize the DirectSound object and buffer pointers to NULL + lpDirectSound = NULL; + lpDSBuffer = NULL; + lpDSCapture = NULL; + lpDSCBuffer = NULL; + + // Define the wave format structure (16-bit PCM, srate, channels) + ZeroMemory(&wfFormat, sizeof(WAVEFORMATEX)); + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = channels; + wfFormat.nSamplesPerSec = (unsigned long) srate; + wfFormat.wBitsPerSample = 8 * sizeof(short); + wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8; + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + + if ( (!strcmp(mode,"play")) || (!strcmp(mode,"duplex")) ) { + DSBUFFERDESC dsbdesc; + HWND hWnd; + + // If using Method 1: + // Define a maximum distance that the write pointer is + // allowed to lead safePos. The size of this zone is + // fairly critical to the behavior of this scheme. The + // value below is set for a 10 millisecond region. + zoneSize = (DWORD) (0.01 * srate * sizeof(short)); // bytes + + // Create the DS object + if ((result = DirectSoundCreate(NULL, &lpDirectSound, NULL)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Cannot open default sound output device!!\n"); + exit(0); + } + + // Get DS device capabilites + DSCAPS dscaps; + ZeroMemory(&dscaps, sizeof(DSCAPS)); + dscaps.dwSize = sizeof(DSCAPS); + if ((result = lpDirectSound->GetCaps(&dscaps)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Cannot get DS device capabilities!!\n"); + exit(0); + } + + // Determine whether primary buffer exists in hardware or software + if (dscaps.dwFlags & DSCAPS_EMULDRIVER) { // Write to secondary buffer + + // Number of buffers of size RT_BUFFER_SIZE to make secondary + // DS buffer. A larger secondary buffer does NOT produce more + // output delay when the write pointer is kept in front of the + // read pointer (Method 1). However, if you use Method 2, this + // variable will be more critical and you'll probably want to + // make it smaller. + int nBufs = 12; + + // Set coooperative level + hWnd = GetForegroundWindow(); + if ((result = lpDirectSound->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't set EXCLUSIVE cooperative level!\n"); + exit(0); + } + + // Even though we will write to the secondary buffer, we need + // to access the primary buffer to set the correct output format. + // The default is 8-bit, 22 kHz! + LPDIRECTSOUNDBUFFER lpdsbPrimary; + + // Setup the DS primary buffer description. + ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC)); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; + + // Obtain the primary buffer + if ((result = lpDirectSound->CreateSoundBuffer(&dsbdesc, + &lpdsbPrimary, NULL)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Cannot get the primary DS buffer address!\n"); + exit(0); + } + + // Set the primary DS buffer sound format. + if ((result = lpdsbPrimary->SetFormat(&wfFormat)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Cannot set the primary DS buffer to proper sound format!\n"); + exit(0); + } + + // Setup the secondary DS buffer description. + dwDSBufSize = RT_BUFFER_SIZE * sizeof(short) * nBufs; + ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC)); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + dsbdesc.dwBufferBytes = dwDSBufSize; + dsbdesc.lpwfxFormat = &wfFormat; + + // Try to create the secondary DS buffer. + if ((result = lpDirectSound->CreateSoundBuffer(&dsbdesc, &lpDSBuffer, NULL)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't create the DS sound buffer!\n"); + exit(0); + } + } + else { // Write to primary buffer + + // Setup the DS primary buffer description. + ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC)); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_STICKYFOCUS; + // Buffer size is determined by sound hardware + dsbdesc.dwBufferBytes = 0; + dsbdesc.lpwfxFormat = NULL; // Must be NULL for primary buffer. + + // Obtain write-primary coooperative level. + hWnd = GetForegroundWindow(); + if ((result = lpDirectSound->SetCooperativeLevel(hWnd, DSSCL_WRITEPRIMARY)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't set WRITEPRIMARY cooperative level!\n"); + exit(0); + } + + // Try to create the primary DS buffer. + if ((result = lpDirectSound->CreateSoundBuffer(&dsbdesc, &lpDSBuffer, NULL)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't create the DS sound buffer!\n"); + exit(0); + } + + // Set primary DS buffer to desired format. + if ((result = lpDSBuffer->SetFormat(&wfFormat)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't set correct format!\n"); + exit(0); + } + } + // Get the buffer size + DSBCAPS dsbcaps; + dsbcaps.dwSize = sizeof(DSBCAPS); + lpDSBuffer->GetCaps(&dsbcaps); + dwDSBufSize = dsbcaps.dwBufferBytes; + + // Lock the DS buffer + if ((result = lpDSBuffer->Lock(0, dwDSBufSize, (LPLPVOID) &pAudioPtr, &dwDataLen, NULL, NULL, 0)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't lock DS sound buffer!\n"); + exit(0); + } + + // Zero the DS buffer + ZeroMemory(pAudioPtr, dwDataLen); + + // Unlock the DS buffer + if ((result = lpDSBuffer->Unlock(pAudioPtr, dwDataLen, NULL, 0)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't unlock DS sound buffer!\n"); + exit(0); + } + } // end of play/duplex initialization + + if ( (!strcmp(mode,"record")) || (!strcmp(mode,"duplex")) ) { + // DirectSound Capture capabilities require DirectX 5.0 or higher + + // The following variable controls the size of the DS Capture + // buffer, which in turns controls the latency in the capture + // process. When dscbufscale = 1, the buffer is equivalent to + // one second of audio. A dscbufscale = 0.5 halves this value. + // Likewise, a dscbufscale = 2 doubles this value. It seems to + // work OK with dscbufscale = 0.5, but there is a periodic + // "clicking" which sometimes occurs. You can go lower, but + // the "clicking" gets worse. Good luck! Yet another reason + // why not to use Windoze. + float dscbufscale = 0.5; + + // Create the DS Capture object + if ((result = DirectSoundCaptureCreate(NULL, &lpDSCapture, NULL)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't open DirectSoundCapture Object!\n"); + fprintf(stderr,"RTSoundIO: Requires DirectX 5 or later.\n"); + exit(0); + } + + // Setup the DS Capture buffer description + DSCBUFFERDESC dscbdesc; + ZeroMemory(&dscbdesc, sizeof(DSCBUFFERDESC)); + dscbdesc.dwSize = sizeof(DSCBUFFERDESC); + dscbdesc.dwFlags = 0; + // Control size of DS Capture buffer here + dwDSCBufSize = (DWORD)(wfFormat.nAvgBytesPerSec*dscbufscale); + dscbdesc.dwBufferBytes = dwDSCBufSize; + dscbdesc.dwReserved = 0; + dscbdesc.lpwfxFormat = &wfFormat; + + // Create the DS Capture buffer + if ((result = lpDSCapture->CreateCaptureBuffer(&dscbdesc, &lpDSCBuffer, NULL)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't create DirectSoundCapture buffer!\n"); + if (result == DSERR_BADFORMAT) { + fprintf(stderr,"RTSoundIO: The input device could not support the desired\n"); + fprintf(stderr,"sample rate (%f), bits per sample (16),\n", srate); + fprintf(stderr,"and/or number of channels (%d).\n", channels); + } + exit(0); + } + + // Lock the DS Capture buffer + if ((result = lpDSCBuffer->Lock(0, dwDSCBufSize, (LPLPVOID) &pAudioPtr, &dwDataLen, NULL, NULL, 0)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't lock DS Capture sound buffer!\n"); + exit(0); + } + + // Zero the DS Capture buffer + ZeroMemory(pAudioPtr, dwDataLen); + + // Unlock the DS Capture buffer + if ((result = lpDSCBuffer->Unlock(pAudioPtr, dwDataLen, NULL, 0)) != DS_OK) { + fprintf(stderr,"RTSoundIO: Couldn't unlock DS Capture sound buffer!\n"); + exit(0); + } + + // Start the DS Capture buffer input + if ((result = lpDSCBuffer->Start(DSCBSTART_LOOPING) != DS_OK)) { + fprintf(stderr,"RTSoundIO: Couldn't start DS Capture sound input!\n"); + exit(0); + } + } // end of record/duplex initialization + + if ( (!strcmp(mode,"play")) || (!strcmp(mode,"duplex")) ) { + // Start the DS buffer playback + if ((result = lpDSBuffer->Play(0, 0, DSBPLAY_LOOPING ) != DS_OK)) { + fprintf(stderr,"RTSoundIO: Couldn't play DS sound buffer!\n"); + exit(0); + } + } +} + +RTSoundIO :: ~RTSoundIO() +{ + // Cleanup the DS buffers + if (lpDSBuffer) { + lpDSBuffer->Stop(); + lpDSBuffer->Release(); + lpDSBuffer = NULL; + } + if (lpDSCBuffer) { + lpDSCBuffer->Stop(); + lpDSCBuffer->Release(); + lpDSCBuffer = NULL; + } + + // Cleanup the DS objects + if (lpDirectSound) { + lpDirectSound->Release(); + lpDirectSound = NULL; + } + if (lpDSCapture) { + lpDSCapture->Release(); + lpDSCapture = NULL; + } +} + +int RTSoundIO :: playBuffer(short *buf, int bufsize) +{ + HRESULT hr; + LPVOID lpbuf1 = NULL; + LPVOID lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + DWORD playPos, safePos; + static UINT nextWritePos = 0; + + // Find out where the read and "safe write" pointers are. + hr = lpDSBuffer->GetCurrentPosition(&playPos, &safePos); + if (hr != DS_OK) return -1; + + // METHOD 1: Keep write pointer in front of read pointer. + // + // Microsloth says that the safePos is about 15 ms ahead of + // playPos. I think this figure is somewhat hardware related, + // especially if you are writing to the primary buffer. With + // my shit-blaster 16, I found the safePos to be about 10 ms + // ahead of playPos. If you really need to reduce delay, you + // can try moving your "safePos" closer to the play pointer. + // You'll be treading on dangerous ground, but then again, + // you're obviously using Windoze so you're already familiar + // with such uncertainty! I've been able to lop off 2-5 ms + // in some circumstances. + //static DWORD backup = (DWORD) (0.005 * SRATE * sizeof(short)); + //safePos = (safePos + dwDSBufSize - backup) % dwDSBufSize; + + // Assume that the next write position is always in front + // of safePos. If not, the write pointer must have wrapped. + // NOTE: If safePos somehow gets ahead of the write pointer, + // then an underrun has occurred and there's not much we can + // do anyway. + DWORD deltaPos; + if( safePos > nextWritePos ) + deltaPos = nextWritePos + dwDSBufSize - safePos; + else + deltaPos = nextWritePos - safePos; + + // Check whether the write pointer is in the allowed region. + while ( deltaPos > zoneSize ) { + // If we are here, then we must wait until the write pointer + // is in the allowed region. For this, we can either + // continuously check the pointer positions until they are + // OK or we can use the Sleep() function to pause operations + // for a certain amount of time. Use of the Sleep() function + // would seem to be the better choice, however, there are + // reports that Sleep() often "sleeps" for much longer than + // requested. I'll let you choose which method to use. + static int sleep = 1; // 1 = sleep, 0 = don't sleep + + if (sleep) { + // Sleep until safePos catches up. Calculate number of + // milliseconds to wait as: + // time = distance * (milliseconds/second) * fudgefactor / + // ((bytes/sample) * (samples/second)) + // A "fudgefactor" less than 1 is used because it was found + // that sleeping too long was MUCH worse than sleeping for + // several shorter periods. + DWORD millis = (DWORD) ((deltaPos * 200.0) / ( sizeof(short) * SRATE)); + + // Sleep for that long + Sleep( millis ); + } + + // Wake up, find out where we are now + hr = lpDSBuffer->GetCurrentPosition( &playPos, &safePos ); + if( hr != DS_OK ) return -1; + + // Backup safePos? (See above) + //safePos = (safePos + dwDSBufSize - backup) % dwDSBufSize; + + if( safePos > nextWritePos ) + deltaPos = nextWritePos + dwDSBufSize - safePos; + else + deltaPos = nextWritePos - safePos; + } + // End of Method 1 + + /* + // METHOD 2: Keep write region behind of play pointer. + if( playPos < nextWritePos ) playPos += dwDSBufSize; // unwrap offset + DWORD endWrite = nextWritePos + bufsize * sizeof(short); + + // Check whether the write region is behind the play pointer. + while ( playPos < endWrite ) { + // If we are here, then we must wait until the play pointer + // gets beyond the write region. For this, we can either + // continuously check the pointer positions until they are + // OK or we can use the Sleep() function to pause operations + // for a certain amount of time. Use of the Sleep() function + // would seem to be the better choice, however, there are + // reports that Sleep() often "sleeps" for much longer than + // requested. I'll let you choose which method to use. + static int sleep = 1; // 1 = sleep, 0 = don't sleep + + if (sleep) { + // Sleep until safePos catches up. Calculate number of + // milliseconds to wait as: + // time = distance * (milliseconds/second) * fudgefactor / + // ((bytes/sample) * (samples/second)) + // A "fudgefactor" less than 1 is used because it was found + // that sleeping too long was MUCH worse than sleeping for + // several shorter periods. + DWORD millis = (DWORD) (((endWrite - playPos) * 200.0) / ( sizeof(short) * SRATE)); + + // Sleep for that long + Sleep( millis ); + } + + // Wake up, find out where we are now + hr = lpDSBuffer->GetCurrentPosition( &playPos, &safePos ); + if( hr != DS_OK ) return -1; + if( playPos < nextWritePos ) playPos += dwDSBufSize; // unwrap offset + } + // End of Method 2. + */ + + // Lock free space in the DS + hr = lpDSBuffer->Lock (nextWritePos, bufsize * sizeof(short), &lpbuf1, &dwsize1, &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) { + // Copy the buffer into the DS + CopyMemory(lpbuf1, buf, dwsize1); + if(NULL != lpbuf2) CopyMemory(lpbuf2, buf+dwsize1, dwsize2); + + // Update our buffer offset and unlock sound buffer + nextWritePos = (nextWritePos + dwsize1 + dwsize2) % dwDSBufSize; + lpDSBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2); + return 0; + } + else return -1; +} + +int RTSoundIO :: recordBuffer(short *buf, int bufsize) +{ + HRESULT hr; + LPVOID lpbuf1 = NULL; + LPVOID lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + DWORD capPos, safePos; + static UINT nextReadPos = 0; + + // Find out where the write and "safe read" pointers are. + hr = lpDSCBuffer->GetCurrentPosition(&capPos, &safePos); + if (hr != DS_OK) return -1; + //printf("capPos = %d, safePos = %d\n", capPos, safePos); + + if( safePos < nextReadPos ) safePos += dwDSCBufSize; // unwrap offset + DWORD endRead = nextReadPos + bufsize * sizeof(short); + //printf("endRead = %d\n", endRead); + + // Check whether the read region is behind the capture pointer. + while ( safePos < endRead ) { + // If we are here, then we must wait until the read pointer + // gets beyond the write region. For this, we can either + // continuously check the pointer positions until they are + // OK or we can use the Sleep() function to pause operations + // for a certain amount of time. Use of the Sleep() function + // would seem to be the better choice, however, there are + // reports that Sleep() often "sleeps" for much longer than + // requested. I'll let you choose which method to use. + static int sleep = 1; // 1 = sleep, 0 = don't sleep + + if (sleep) { + // Sleep until safePos catches up. Calculate number of + // milliseconds to wait as: + // time = distance * (milliseconds/second) * fudgefactor / + // ((bytes/sample) * (samples/second)) + // A "fudgefactor" less than 1 is used because it was found + // that sleeping too long was MUCH worse than sleeping for + // several shorter periods. + DWORD millis = (DWORD) (((endRead - safePos) * 200.0) / ( sizeof(short) * SRATE)); + + // Sleep for that long + Sleep( millis ); + } + + // Wake up, find out where we are now + hr = lpDSCBuffer->GetCurrentPosition( &capPos, &safePos ); + if( hr != DS_OK ) return -1; + //printf("capPos = %d, safePos = %d\n", capPos, safePos); + if( safePos < nextReadPos ) safePos += dwDSCBufSize; // unwrap offset + } + //printf("how about here?\n"); + // Lock free space in the DS Capture buffer + hr = lpDSCBuffer->Lock(nextReadPos, bufsize * sizeof(short), &lpbuf1, &dwsize1, &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) { + // Copy the DS Capture data to the buffer + CopyMemory(buf, lpbuf1, dwsize1); + if(NULL != lpbuf2) CopyMemory(buf+dwsize1, lpbuf2, dwsize2); + + // Update our buffer offset and unlock sound buffer + nextReadPos = (nextReadPos + dwsize1 + dwsize2) % dwDSCBufSize; + lpDSCBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2); + return 0; + } + else return -1; +} + +#elif (defined(__STK_REALTIME_) && defined(__WINMM_API_) ) + +#include + +#define FRAMETIME (long) (1000.0 * RT_BUFFER_SIZE / SRATE) + +RTSoundIO :: RTSoundIO(MY_FLOAT srate, int channels, char *mode) +{ + MMRESULT result; + WAVEFORMATEX wfx; + int bufferSize = RT_BUFFER_SIZE; + + audioPort = NULL; + + if ( (!strcmp(mode,"record")) || (!strcmp(mode,"duplex")) ) { + fprintf(stderr,"Sorry ... no audio input support under WinMM API!\n"); + exit(0); + } + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = channels; + wfx.nSamplesPerSec = (unsigned long) srate; + wfx.nBlockAlign = sizeof(short) * channels; + wfx.nAvgBytesPerSec = (unsigned long) srate * wfx.nBlockAlign; + wfx.wBitsPerSample = 8 * sizeof(short); + wfx.cbSize = 0; + + /* Open a Wave Out device using the wave mapper to guide us */ + result = waveOutOpen(&audioPort,WAVE_MAPPER,&wfx,(DWORD)NULL,(DWORD)NULL,CALLBACK_NULL); + if (result != MMSYSERR_NOERROR) { + fprintf(stderr,"RTSoundIO: Cannot open audio port (WinMM API)!\n"); + exit(0); + } + + for( outBufNum = 0; outBufNum < (UINT)NUM_OUT_BUFFERS; outBufNum++ ) + { + /* set up a couple of wave headers */ + whOut[outBufNum].lpData = (LPSTR)calloc(channels*bufferSize, sizeof(short)); + if (whOut[outBufNum].lpData == NULL){ + waveOutClose( audioPort ); + fprintf(stderr,"RTSoundIO: Error initializing audio buffers (WinMM API)!\n"); + exit(0); + } + whOut[outBufNum].dwBufferLength = channels*bufferSize*sizeof(short); + whOut[outBufNum].dwBytesRecorded = 0; + whOut[outBufNum].dwUser = 1; + //whOut[outBufNum].dwFlags = 0; + whOut[outBufNum].dwFlags = WHDR_DONE; + whOut[outBufNum].dwLoops = 0; + whOut[outBufNum].lpNext = NULL; + whOut[outBufNum].reserved = 0; + } + + /* Write the first buffer out to get things going */ + outBufNum = 0; + result = waveOutPrepareHeader(audioPort, &whOut[outBufNum],sizeof(WAVEHDR)); + result = waveOutWrite(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); + + /* Keep track of time so that we know how long we can sleep */ + lastWriteTime = timeGetTime(); +} + +RTSoundIO :: ~RTSoundIO() +{ + MMRESULT result; + long timeToGo; + + /* Close Audio Port */ + if (audioPort != NULL) { + result = waveOutReset(audioPort); + for( outBufNum = 0; outBufNum < (UINT)NUM_OUT_BUFFERS; outBufNum++ ) + { + /* Loop until the next waveheader indicates that we are done */ + while( !(whOut[outBufNum].dwFlags & WHDR_DONE) ) + { + //printf("."); + timeToGo = (long) (FRAMETIME - (timeGetTime()-lastWriteTime)); + if( timeToGo > 0 ) Sleep( (long) timeToGo ); + } + /* Unprepare the header */ + result = waveOutUnprepareHeader(audioPort, &whOut[outBufNum],sizeof(WAVEHDR)); + if (whOut[outBufNum].lpData != NULL) { + free(whOut[outBufNum].lpData); + whOut[outBufNum].lpData = NULL; + } + } + result = waveOutClose(audioPort); + } +} + +int RTSoundIO :: playBuffer(short *buf, int bufsize) +{ + MMRESULT result; + long timeToGo; + + outBufNum++; + if( outBufNum >= (UINT)NUM_OUT_BUFFERS ) outBufNum = 0; + + /* Loop until the next waveheader indicates that we are done */ + while( !(whOut[outBufNum].dwFlags & WHDR_DONE) ) + { + //printf("."); + timeToGo = (long) (FRAMETIME - (timeGetTime()-lastWriteTime)); + //timeToGo = (long) (FRAMETIME * 0.5); + if( timeToGo > 0 ) Sleep( (long) timeToGo ); + } + result = waveOutUnprepareHeader(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); + + memcpy( whOut[outBufNum].lpData, buf, bufsize*sizeof(short)); + result = waveOutPrepareHeader(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); + result = waveOutWrite(audioPort, &whOut[outBufNum], sizeof(WAVEHDR)); + lastWriteTime = timeGetTime(); + + return 0; +} + +int RTSoundIO :: recordBuffer(short *buf, int bufsize) +{ + // There is no current support for audio input under the WinMM API ... sorry! + return -1; +} + +#endif diff --git a/STK/RTSoundIO.h b/STK/RTSoundIO.h new file mode 100644 index 0000000..7a2045d --- /dev/null +++ b/STK/RTSoundIO.h @@ -0,0 +1,71 @@ +/******************************************/ +/* RTSoundIO.cpp */ +/* Realtime Sound I/O Object for STK, */ +/* by Gary P. Scavone, 1998-1999. */ +/* */ +/* The sound output sections of this */ +/* object were based in part on code */ +/* by Doug Scott (SGI), Tim Stilson */ +/* (Linux), Bill Putnam (Win Wav), and */ +/* R. Marsanyi (DirectSound). */ +/* */ +/* This object provides a standard API */ +/* across all platforms for STK realtime */ +/* audio input/output. The sound output */ +/* code is fairly robust. Audio input, */ +/* however, is more dependent on the */ +/* capabilities of the particular OS and */ +/* the soundcard being used. For the */ +/* moment, I'll try to provide 1 or 2 */ +/* channel support. */ +/* */ +/* 16-bit integer audio input/output */ +/* data is being assumed. */ +/******************************************/ + +#if !defined(__RTSOUNDIO_h) +#define __RTSOUNDIO_h + +#include "Object.h" + +#if defined(__OS_IRIX_) + #include +#elif defined(__WINDS_API_) + #include + #include +#elif defined(__WINMM_API_) + #include + #include + #define NUM_OUT_BUFFERS 6 +#endif + +class RTSoundIO : public Object +{ + protected: +#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_)) + ALport audio_port_in; + ALport audio_port_out; +#elif (defined(__STK_REALTIME_) && defined(__OSS_API_)) + int audio_fd; +#elif (defined(__STK_REALTIME_) && defined(__WINDS_API_) ) + LPDIRECTSOUND lpDirectSound; + LPDIRECTSOUNDBUFFER lpDSBuffer; + DWORD dwDSBufSize; + DWORD zoneSize; + LPDIRECTSOUNDCAPTURE lpDSCapture; + LPDIRECTSOUNDCAPTUREBUFFER lpDSCBuffer; + DWORD dwDSCBufSize; +#elif (defined(__STK_REALTIME_) && defined(__WINMM_API_) ) + HWAVEOUT audioPort; + WAVEHDR whOut[NUM_OUT_BUFFERS]; + UINT outBufNum; + DWORD lastWriteTime; +#endif + public: + RTSoundIO(MY_FLOAT srate, int channels, char *mode); + ~RTSoundIO(); + int playBuffer(short *buf, int bufsize); + int recordBuffer(short *buf, int bufsize); +}; + +#endif diff --git a/STK/RTWvIn.cpp b/STK/RTWvIn.cpp new file mode 100644 index 0000000..1e7b190 --- /dev/null +++ b/STK/RTWvIn.cpp @@ -0,0 +1,126 @@ +/*******************************************/ +/* RTWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to read in realtime 16-bit data */ +/* from a computer's audio port. */ +/* */ +/* NOTE: This object is NOT intended for */ +/* use in achieving simultaneous realtime */ +/* audio input/output (together with */ +/* RTWvOut). Under certain circumstances */ +/* such a scheme is possible, though you */ +/* should definitely know what you are */ +/* doing before trying. For safer "full- */ +/* duplex" operation, use the RTDuplex */ +/* class. */ +/*******************************************/ + +#include "RTWvIn.h" + +RTWvIn :: RTWvIn(MY_FLOAT srate, short chans) +{ + soundIO = new RTSoundIO(srate, chans, "record"); + length = RT_BUFFER_SIZE; + channels = chans; + data = 0; + rtdata = (short *) new short[channels*(RT_BUFFER_SIZE+1)]; + + this->getMoreData(); + + rate = (MY_FLOAT) srate / SRATE; + if (fmod(rate, 1.0) > 0.0) interpolate = 1; + else interpolate = 0; + looping = 0; + time = (MY_FLOAT) 0.0; + phaseOffset = (MY_FLOAT) 0.0; + finished = 0; + lastOutput = (MY_FLOAT *) calloc(channels, sizeof(MY_FLOAT)); + gain = 0.00003052; +#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_)) + // This is necessary under IRIX because it scales the input by 0.5 + // when using single-channel input. + if (channels == 1) gain *= 2; +#endif +} + +RTWvIn :: ~RTWvIn() +{ + delete soundIO; + if (rtdata) { + delete [ ] rtdata; + rtdata = 0; + } +} + +void RTWvIn :: normalize() +{ + /* Do nothing ... cannot normalize realtime input */ +} + +void RTWvIn :: normalize(MY_FLOAT newPeak) +{ + /* Do nothing ... cannot normalize realtime input */ +} + +void RTWvIn :: addPhaseOffset(MY_FLOAT anAngle) +{ + /* No phaseOffset for realtime input */ + phaseOffset = 0.0; +} + +void RTWvIn :: setLooping(int aLoopStatus) +{ + /* Cannot loop realtime input */ + looping = 0; +} + +void RTWvIn :: getMoreData() +{ + soundIO->recordBuffer(rtdata,(RT_BUFFER_SIZE)*channels); + long temp = channels*(RT_BUFFER_SIZE); + for (int i=0;i= length) { + this->getMoreData(); + } + while (time >= length) /* Check for end of sound */ + time -= length; /* loop back to beginning */ + while (time < 0.0) /* Check for end of sound */ + time += length; /* loop back to beginning */ + + return finished; +} diff --git a/STK/RTWvIn.h b/STK/RTWvIn.h new file mode 100644 index 0000000..8078dbf --- /dev/null +++ b/STK/RTWvIn.h @@ -0,0 +1,44 @@ +/*******************************************/ +/* RTWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to read in realtime 16-bit data */ +/* from a computer's audio port. */ +/* */ +/* NOTE: This object is NOT intended for */ +/* use in achieving simultaneous realtime */ +/* audio input/output (together with */ +/* RTWvOut). Under certain circumstances */ +/* such a scheme is possible, though you */ +/* should definitely know what you are */ +/* doing before trying. For safer "full- */ +/* duplex" operation, use the RTDuplex */ +/* class. */ +/*******************************************/ + +#if !defined(__RTWvIn_h) +#define __RTWvIn_h + +#include "Object.h" +#include "RTSoundIO.h" +#include "WvIn.h" + +class RTWvIn : public WvIn +{ + protected: + RTSoundIO *soundIO; + short *rtdata; + MY_FLOAT gain; + public: + RTWvIn(MY_FLOAT srate, short chans); + ~RTWvIn(); + void normalize(); + void normalize(MY_FLOAT newPeak); + void addPhaseOffset(MY_FLOAT anAngle); + void setLooping(int aLoopStatus); + void getMoreData(); + int informTick(); +}; + +#endif diff --git a/STK/RTWvOut.cpp b/STK/RTWvOut.cpp new file mode 100644 index 0000000..af2b074 --- /dev/null +++ b/STK/RTWvOut.cpp @@ -0,0 +1,58 @@ +/*******************************************/ +/* Real-Time Wave File Output Class, */ +/* by Perry R. Cook, 1996 */ +/* revised by Gary P. Scavone, 1999 */ +/* */ +/* This object opens a realtime soundout */ +/* device, and pokes buffers of samples */ +/* into it. */ +/*******************************************/ + +#include "RTWvOut.h" + +#if defined(__STK_REALTIME_) + +RTWvOut :: RTWvOut(MY_FLOAT srate, int chans) +{ + // We'll let RTSoundIO deal with channel and srate limitations. + channels = chans; + soundIO = new RTSoundIO(srate, channels, "play"); + counter = 0; +} + +RTWvOut :: ~RTWvOut() +{ + soundIO->playBuffer(data,counter); + counter = 0; + while (counterplayBuffer(data,counter); + soundIO->playBuffer(data,counter); // Are these extra writes necessary? + soundIO->playBuffer(data,counter); + delete soundIO; +} + +void RTWvOut :: tick(MY_FLOAT sample) +{ + for (int i=0;i= RT_BUFFER_SIZE) { + soundIO->playBuffer(data,counter); + counter = 0; + } +} + +void RTWvOut :: mtick(MY_MULTI samples) +{ + for (int i=0;i= RT_BUFFER_SIZE) { + soundIO->playBuffer(data,counter); + counter = 0; + } +} + +#endif diff --git a/STK/RTWvOut.h b/STK/RTWvOut.h new file mode 100644 index 0000000..6b2be33 --- /dev/null +++ b/STK/RTWvOut.h @@ -0,0 +1,32 @@ +/*******************************************/ +/* Real-Time Wave File Output Class, */ +/* by Perry R. Cook, 1996 */ +/* revised by Gary P. Scavone, 1999 */ +/* */ +/* This object opens a realtime soundout */ +/* device, and pokes buffers of samples */ +/* into it. */ +/*******************************************/ + +#include "Object.h" +#include "WvOut.h" +#include "RTSoundIO.h" + +#if !defined(__RTWvOut_h) +#define __RTWvOut_h + +class RTWvOut : public WvOut +{ + protected: + RTSoundIO *soundIO; + short data[RT_BUFFER_SIZE]; + long counter; + int channels; + public: + RTWvOut(MY_FLOAT srate, int chans); + ~RTWvOut(); + void tick(MY_FLOAT sample); + void mtick(MY_MULTI samples); +}; + +#endif // defined(__RTWvOut_h) diff --git a/STK/RawWvIn.cpp b/STK/RawWvIn.cpp new file mode 100644 index 0000000..f1cc068 --- /dev/null +++ b/STK/RawWvIn.cpp @@ -0,0 +1,79 @@ +/*******************************************/ +/* RawWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to open raw 16-bit data (signed */ +/* integer) files for playback. */ +/* */ +/* STK RawWave files are assumed to be */ +/* monaural and big-endian. */ +/*******************************************/ + +#include "RawWvIn.h" +#include +#include + +#ifdef __LITTLE_ENDIAN__ + #include "swapstuf.h" +#endif + +RawWvIn :: RawWvIn(char *fileName, char *mode) +{ + // Use the system call "stat" to determine the file length + struct stat filestat; + if (stat(fileName, &filestat) == -1) + { /* Opening file failed */ + fprintf(stderr,"Cannot access or find rawwave file: %s !!!\n",fileName); + exit(0); + } + length = (long) filestat.st_size / 2; // length in 2-byte samples + + // Open the file and read samples into data[] + FILE *fd; + fd = fopen(fileName,"rb"); + if (!fd) { + printf("Couldn't open or find rawwave file %s !!!\n",fileName); + exit(0); + } + + channels = 1; // All STK rawwave files are mono + data = (MY_FLOAT *) new MY_FLOAT[(length+1)*channels]; + + long i = 0; + INT16 temp; + fseek(fd,0,SEEK_SET); // Only here to bypass bug in Linux glibc 2.1x (RedHat 6.0) + while (fread(&temp,2,1,fd)) { +#ifdef __LITTLE_ENDIAN__ + temp = SwapINT16 (temp); +#endif + data[i++] = (MY_FLOAT) temp; + } + fclose(fd); + + // Setup for looping or one-shot playback + if (!strcmp(mode,"looping")) { + looping = 1; + data[length] = data[0]; // extra sample for interpolation + } + else if (!strcmp(mode,"oneshot")) { + looping = 0; + data[length] = data[length-1]; // extra sample for interpolation + } + else { + fprintf(stderr,"ERROR: Unsupported RawWvIn mode: %s\n",mode); + free(data); + exit(0); + } + + time = (MY_FLOAT) 0.0; + phaseOffset = (MY_FLOAT) 0.0; + rate = (MY_FLOAT) 1.0; + interpolate = 0; + finished = 0; + lastOutput = (MY_FLOAT *) calloc(channels, sizeof(MY_FLOAT)); +} + +RawWvIn :: ~RawWvIn() +{ +} diff --git a/STK/RawWvIn.h b/STK/RawWvIn.h new file mode 100644 index 0000000..ca79e23 --- /dev/null +++ b/STK/RawWvIn.h @@ -0,0 +1,26 @@ +/*******************************************/ +/* RawWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to open raw 16-bit data (signed */ +/* integer) files for playback. */ +/* */ +/* STK RawWave files are assumed to be */ +/* big-endian. */ +/*******************************************/ + +#if !defined(__RawWvIn_h) +#define __RawWvIn_h + +#include "Object.h" +#include "WvIn.h" + +class RawWvIn : public WvIn +{ + public: + RawWvIn(char *fileName, char *mode); + ~RawWvIn(); +}; + +#endif diff --git a/STK/RawWvOut.cpp b/STK/RawWvOut.cpp new file mode 100644 index 0000000..0b34f21 --- /dev/null +++ b/STK/RawWvOut.cpp @@ -0,0 +1,106 @@ +/*******************************************/ +/* RawWvOut Output Class */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object spits samples into a raw */ +/* 16-bit data (signed integer) file. */ +/* */ +/* STK RawWave files are assumed to be */ +/* monaural and big-endian. */ +/*******************************************/ + +#include "RawWvOut.h" + +#ifdef __LITTLE_ENDIAN__ + #include "swapstuf.h" +#endif + +RawWvOut :: RawWvOut(char *fileName) +{ + char tempName[128]; + + channels = 1; + strcpy(tempName,fileName); + if (strstr(tempName,".raw") == NULL) strcat(tempName,".raw"); + fd = fopen(tempName,"wb"); + if (!fd) { + printf("Couldn't create soundfile %s !!!!!!!!\n",fileName); + exit(0); + } + printf("\nCreating rawwave file %s\n\n", tempName); + + counter = 0; + totalCount = 0; +} + +RawWvOut :: RawWvOut(int chans, char *fileName) +{ + char tempName[128]; + + if (chans > 1) { + fprintf(stderr,"RawWvOut: Unsupported # of channels: %d\n", chans); + exit(0); + } + channels = chans; + strcpy(tempName,fileName); + strcat(tempName,".raw"); + fd = fopen(tempName,"wb"); + if (!fd) { + printf("Couldn't create soundfile %s !!!!!!!!\n",fileName); + exit(0); + } + printf("Creating soundfile %s.\n", tempName); + + counter = 0; + totalCount = 0; +} + +RawWvOut :: ~RawWvOut() +{ + double temp; + + fwrite(data,2,counter,fd); + temp = (double) totalCount * ONE_OVER_SRATE; + printf("%f Seconds Computed\n",temp); + fclose(fd); +} + +long RawWvOut :: getCounter() +{ + return totalCount; +} + +MY_FLOAT RawWvOut :: getTime() +{ + return (MY_FLOAT) totalCount * ONE_OVER_SRATE; +} + +void RawWvOut :: tick(MY_FLOAT sample) +{ + data[counter] = (INT16) (sample * 32000.0); +#ifdef __LITTLE_ENDIAN__ + data[counter] = SwapINT16 (data[counter]); +#endif + counter++; + + totalCount++; + if (counter == RAW_BUFFER_SIZE) { + fwrite(data,2,RAW_BUFFER_SIZE,fd); + counter = 0; + } +} + +void RawWvOut :: mtick(MY_MULTI samples) +{ + data[counter] = (INT16) (*samples * 32000.0); +#ifdef __LITTLE_ENDIAN__ + data[counter] = SwapINT16 (data[counter]); +#endif + counter++; + + totalCount++; + if (counter == RAW_BUFFER_SIZE) { + fwrite(data,2,RAW_BUFFER_SIZE,fd); + counter = 0; + } +} diff --git a/STK/RawWvOut.h b/STK/RawWvOut.h new file mode 100644 index 0000000..9d96e3d --- /dev/null +++ b/STK/RawWvOut.h @@ -0,0 +1,38 @@ +/*******************************************/ +/* RawWvOut Output Class */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object spits samples into a raw */ +/* 16-bit data (signed integer) file. */ +/* */ +/* STK RawWave files are assumed to be */ +/* monaural and big-endian. */ +/*******************************************/ + +#include "Object.h" +#include "WvOut.h" + +#if !defined(__RawWvOut_h) +#define __RawWvOut_h + +#define RAW_BUFFER_SIZE 1024 + +class RawWvOut : public WvOut +{ + protected: + FILE *fd; + INT16 data[RAW_BUFFER_SIZE]; + long counter; + long totalCount; + int channels; + public: + RawWvOut(char *fileName); + RawWvOut(int chans, char *fileName); + ~RawWvOut(); + long getCounter(); + MY_FLOAT getTime(); + void tick(MY_FLOAT sample); + void mtick(MY_MULTI samples); +}; + +#endif // defined(__RawWvOut_h) diff --git a/ReedTabl.cpp b/STK/ReedTabl.cpp similarity index 100% rename from ReedTabl.cpp rename to STK/ReedTabl.cpp diff --git a/ReedTabl.h b/STK/ReedTabl.h similarity index 92% rename from ReedTabl.h rename to STK/ReedTabl.h index b50d32f..da695e7 100644 --- a/ReedTabl.h +++ b/STK/ReedTabl.h @@ -6,6 +6,9 @@ /* more for information. */ /**********************************************/ +#if !defined(__ReedTabl_h) +#define __ReedTabl_h + #include "Object.h" class ReedTabl : public Object @@ -22,3 +25,5 @@ class ReedTabl : public Object MY_FLOAT lookup(MY_FLOAT deltaP); MY_FLOAT lastOut(); }; + +#endif diff --git a/Release/.placeholder b/STK/Release/.placeholder similarity index 100% rename from Release/.placeholder rename to STK/Release/.placeholder diff --git a/Reverb.cpp b/STK/Reverb.cpp similarity index 100% rename from Reverb.cpp rename to STK/Reverb.cpp diff --git a/Reverb.h b/STK/Reverb.h similarity index 100% rename from Reverb.h rename to STK/Reverb.h diff --git a/STK/Rhodey.cpp b/STK/Rhodey.cpp new file mode 100644 index 0000000..0f9b092 --- /dev/null +++ b/STK/Rhodey.cpp @@ -0,0 +1,66 @@ +/******************************************/ +/* Fender Rhodes Electric Piano Subclass */ +/* of Algorithm 5 (TX81Z) Subclass of */ +/* 4 Operator FM Synth */ +/* by Perry R. Cook, 1995-96 */ +/******************************************/ + +#include "Rhodey.h" + +Rhodey :: Rhodey() : FM4Alg5() +{ + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file1[128]; + char file2[128]; + char file3[128]; + char file4[128]; + strcpy(file1, RAWWAVE_PATH); + strcpy(file2, RAWWAVE_PATH); + strcpy(file3, RAWWAVE_PATH); + strcpy(file4, RAWWAVE_PATH); + this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"), + strcat(file2,"rawwaves/sinewave.raw"), + strcat(file3,"rawwaves/sinewave.raw"), + strcat(file4,"rawwaves/fwavblnk.raw")); + this->setRatio(0,(MY_FLOAT) 1.0); + this->setRatio(1,(MY_FLOAT) 0.5); + this->setRatio(2,(MY_FLOAT) 1.0); + this->setRatio(3,(MY_FLOAT) 15.0); + gains[0] = __FM4Op_gains[99]; + gains[1] = __FM4Op_gains[90]; + gains[2] = __FM4Op_gains[99]; + gains[3] = __FM4Op_gains[67]; + adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.00,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[3]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.25,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + twozero->setGain((MY_FLOAT) 1.0); +} + +Rhodey :: ~Rhodey() +{ + +} + +void Rhodey :: setFreq(MY_FLOAT frequency) +{ + baseFreq = frequency * (MY_FLOAT) 2.0; + waves[0]->setFreq(baseFreq * ratios[0]); + waves[1]->setFreq(baseFreq * ratios[1]); + waves[2]->setFreq(baseFreq * ratios[2]); + waves[3]->setFreq(baseFreq * ratios[3]); +} + +void Rhodey :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + gains[0] = amp * __FM4Op_gains[99]; + gains[1] = amp * __FM4Op_gains[90]; + gains[2] = amp * __FM4Op_gains[99]; + gains[3] = amp * __FM4Op_gains[67]; + this->setFreq(freq); + this->keyOn(); +#if defined(_debug_) + printf("Rhodey : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + diff --git a/Rhodey.h b/STK/Rhodey.h similarity index 100% rename from Rhodey.h rename to STK/Rhodey.h diff --git a/STK/SKINI11.cpp b/STK/SKINI11.cpp new file mode 100644 index 0000000..598f87f --- /dev/null +++ b/STK/SKINI11.cpp @@ -0,0 +1,344 @@ +/******************************************/ +/* 3nd generation SKINI Text File Reader */ +/* Class, by Perry R. Cook, 1999 */ +/* This Object can open a SKINI File */ +/* and parse it. The file spec is mine */ +/* and mine alone, but it's all text so */ +/* that should help you figuring it out. */ +/* */ +/* SKINI (Synthesis toolKit Instrument */ +/* Network Interface) is like MIDI, but */ +/* allows for floating point control */ +/* changes, note numbers, etc. Example: */ +/* noteOn 60.01 111.132 plays a sharp */ +/* middle C with a velocity of 111.132 */ +/* See SKINI11.txt for more information */ +/* */ +/******************************************/ + +#include "SKINI11.h" + +SKINI11 :: SKINI11(char *fileName) /* Constructor for reading SKINI files */ +{ /* Use nextMessage() method */ + myFile = fopen(fileName,"r"); + if ((int) myFile < 0) printf("SKINI11: Can't open SKINI score file\n"); + this->nextMessage(); +} + +SKINI11 :: SKINI11() /* Constructor to use this object for parsing */ +{ /* SKINI Strings (coming over socket for example */ +} /* Use parseThis() method with string argument */ + +SKINI11 :: ~SKINI11() +{ +} + +/***************** SOME HANDY ROUTINES *******************/ + +#include "SKINI11.tbl" + +#define __SK_MAX_FIELDS_ 5 +#define __SK_MAX_SIZE_ 32 + +short ignore(char aChar) +{ + short ignoreIt = 0; + if (aChar == 0) ignoreIt = 1; // Null String Termination + if (aChar == '\n') ignoreIt = 1; // Carraige Return??? + if (aChar == '/') ignoreIt = 2; // Comment Line + return ignoreIt; +} + +short delimit(char aChar) +{ + if (aChar == ' ' || // Space + aChar == ',' || // Or Comma + aChar == '\t') // Or Tab + return 1; + else + return 0; +} + +short nextChar(char* aString) +{ + int i; + + for (i=0;i<__SK_MAX_SIZE_;i++) { + if ( aString[i] != ' ' && // Space + aString[i] != ',' && // Or Comma + aString[i] != '\t' ) // Or Tab + return i; + } + return 1024; +} + +int subStrings(char *aString, + char someStrings[__SK_MAX_FIELDS_][__SK_MAX_SIZE_], + int somePointrs[__SK_MAX_FIELDS_], + char *remainderString) +{ + int notDone,howMany,point,temp; + notDone = 1; + howMany = 0; + point = 0; + temp = nextChar(aString); + if (temp >= __SK_MAX_SIZE_) { + notDone = 0; + printf("Confusion here: Ignoring this line\n"); + printf("%s\n",aString); + return howMany; + } + point = temp; + somePointrs[howMany] = point; + temp = 0; + while (notDone) { + if (aString[point] == '\n') { + notDone = 0; + } + else { + someStrings[howMany][temp++] = aString[point++]; + if (temp >= __SK_MAX_SIZE_) { + howMany = 0; + return howMany; + } + if (delimit(aString[point]) || aString[point] == '\n') { + someStrings[howMany][temp] = 0; + howMany += 1; + if (howMany < __SK_MAX_FIELDS_) { + temp = nextChar(&aString[point]); + point += temp; + somePointrs[howMany-1] = point; + temp = 0; + } + else { + temp = 0; + somePointrs[howMany-1] = point; + while(aString[point] != '\n') + remainderString[temp++] = aString[point++]; + remainderString[temp] = aString[point]; + } + } + } + } + // printf("Got: %i Strings:\n",howMany); + // for (temp=0;temp 0) + which = 0; + aField = 0; + strcpy(msgTypeString,someStrings[aField]); + while ((which < __SK_MaxMsgTypes_) && + (strcmp(msgTypeString, + skini_msgs[which].messageString))) { + which += 1; + } + if (which >= __SK_MaxMsgTypes_) { + messageType = -1; + printf("Couldn't parse this message field: =%s\n %s\n", + msgTypeString,aString); + return messageType; + } + else { + messageType = skini_msgs[which].type; + // printf("Message Token = %s type = %i\n", msgTypeString,messageType); + } + aField += 1; + + if (someStrings[0][0] == '=') { + deltaTime = (MY_FLOAT) atof(&someStrings[aField][1]); + deltaTime = -deltaTime; + } + else { + deltaTime = (MY_FLOAT) atof(someStrings[aField]); + } + // printf("DeltaTime = %f\n",deltaTime); + aField += 1; + + channel = atoi(someStrings[aField]); + // printf("Channel = %i\n",channel); + aField += 1; + + if (skini_msgs[which].data2 != NOPE) { + if (skini_msgs[which].data2 == SK_INT) { + byteTwoInt = atoi(someStrings[aField]); + byteTwo = (MY_FLOAT) byteTwoInt; + } + else if (skini_msgs[which].data2 == SK_DBL) { + byteTwo = (MY_FLOAT) atof(someStrings[aField]); + byteTwoInt = (long) byteTwo; + } + else if (skini_msgs[which].data2 == SK_STR) { + temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */ + temp2 = 0; + while (aString[temp] != '\n') { + remainderString[temp2++] = aString[temp++]; + } + remainderString[temp2] = 0; + } + else { + byteTwoInt = skini_msgs[which].data2; + byteTwo = (MY_FLOAT) byteTwoInt; + aField -= 1; + } + + aField += 1; + if (skini_msgs[which].data3 != NOPE) { + if (skini_msgs[which].data3 == SK_INT) { + byteThreeInt = atoi(someStrings[aField]); + byteThree = (MY_FLOAT) byteThreeInt; + } + else if (skini_msgs[which].data3 == SK_DBL) { + byteThree = (MY_FLOAT) atof(someStrings[aField]); + byteThreeInt = (long) byteThree; + } + else if (skini_msgs[which].data3 == SK_STR) { + temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */ + temp2 = 0; + while (aString[temp] != '\n') { + remainderString[temp2++] = aString[temp++]; + } + remainderString[temp2] = 0; + } + else { + byteThreeInt = skini_msgs[which].data3; + byteThree = (MY_FLOAT) byteThreeInt; + } + } + else { + byteThreeInt = byteTwoInt; + byteThree = byteTwo; + } + } + } + return messageType; +} + +long SKINI11 :: nextMessage() +{ + int notDone; + char inputString[1024]; + + notDone = 1; + while (notDone) { + notDone = 0; + if (!fgets(inputString,1024,myFile)) { + printf("//End of Score. Thanks for using SKINI0.9 Bye Bye!!\n"); + messageType = -1; + return messageType; + } + else if (parseThis(inputString) == 0) { + notDone = 1; + } + } + return messageType; +} + +long SKINI11 :: getType() +{ + return messageType; +} + +long SKINI11 :: getChannel() +{ + return channel; +} + +MY_FLOAT SKINI11 :: getDelta() +{ + return deltaTime; +} + +MY_FLOAT SKINI11 :: getByteTwo() +{ + return byteTwo; +} + +long SKINI11 :: getByteTwoInt() +{ + return byteTwoInt; +} + +MY_FLOAT SKINI11 :: getByteThree() +{ + return byteThree; +} + +long SKINI11 :: getByteThreeInt() +{ + return byteThreeInt; +} + +char* SKINI11 :: getRemainderString() +{ + return remainderString; +} + +char* SKINI11 :: getMessageTypeString() +{ + return msgTypeString; +} + +char sk_tempString[1024]; + +char* SKINI11 :: whatsThisType(long type) +{ + int i = 0; + sk_tempString[0] = 0; + for (i=0;i<__SK_MaxMsgTypes_;i++) { + if (type == skini_msgs[i].type) { + strcat(sk_tempString,skini_msgs[i].messageString); + strcat(sk_tempString,","); + } + } + return sk_tempString; +} + +char* SKINI11 :: whatsThisController(long contNum) +{ + int i = 0; + sk_tempString[0] = 0; + for (i=0;i<__SK_MaxMsgTypes_;i++) { + if (skini_msgs[i].type == __SK_ControlChange_ + && contNum == skini_msgs[i].data2) { + strcat(sk_tempString,skini_msgs[i].messageString); + strcat(sk_tempString,","); + } + } + return sk_tempString; +} + +/************ Test Main Program *****************/ +/* +void main(int argc,char *argv[]) +{ + SKINI11 testFile(argv[1]); + + while(testFile.nextMessage() > 0) ; + +} +*/ + diff --git a/SKINI11.h b/STK/SKINI11.h similarity index 97% rename from SKINI11.h rename to STK/SKINI11.h index 5e2b283..65965be 100644 --- a/SKINI11.h +++ b/STK/SKINI11.h @@ -1,6 +1,6 @@ /******************************************/ /* 3rd generation SKINI Text File Reader */ -/* Class, by Perry R. Cook, 1997 */ +/* Class, by Perry R. Cook, 1999 */ /* This Object can open a SKINI File */ /* and parse it. The file spec is mine */ /* and mine alone, but it's all text so */ diff --git a/SKINI11.msg b/STK/SKINI11.msg similarity index 100% rename from SKINI11.msg rename to STK/SKINI11.msg diff --git a/SKINI11.tbl b/STK/SKINI11.tbl similarity index 100% rename from SKINI11.tbl rename to STK/SKINI11.tbl diff --git a/SamplFlt.cpp b/STK/SamplFlt.cpp similarity index 100% rename from SamplFlt.cpp rename to STK/SamplFlt.cpp diff --git a/SamplFlt.h b/STK/SamplFlt.h similarity index 71% rename from SamplFlt.h rename to STK/SamplFlt.h index dd58117..1609d23 100644 --- a/SamplFlt.h +++ b/STK/SamplFlt.h @@ -16,14 +16,14 @@ class SamplFlt : public Sampler { - protected: - FormSwep *filters[2]; - TwoZero *twozeroes[2]; - public: - SamplFlt(); - virtual ~SamplFlt(); - virtual MY_FLOAT tick(); - virtual void controlChange(int number, MY_FLOAT value); + protected: + FormSwep *filters[2]; + TwoZero *twozeroes[2]; + public: + SamplFlt(); + virtual ~SamplFlt(); + virtual MY_FLOAT tick(); + virtual void controlChange(int number, MY_FLOAT value); }; #endif diff --git a/Sampler.cpp b/STK/Sampler.cpp similarity index 50% rename from Sampler.cpp rename to STK/Sampler.cpp index 3b26df0..5f828b9 100644 --- a/Sampler.cpp +++ b/STK/Sampler.cpp @@ -10,38 +10,38 @@ Sampler :: Sampler() { - adsr = new ADSR; - /* We don't make the waves here yet, because */ - /* we don't know what they will be. */ - baseFreq = (MY_FLOAT) 440.0; - filter = new OnePole; - attackGain = (MY_FLOAT) 0.25; - loopGain = (MY_FLOAT) 0.25; - whichOne = 0; + adsr = new ADSR; + /* We don't make the waves here yet, because */ + /* we don't know what they will be. */ + baseFreq = (MY_FLOAT) 440.0; + filter = new OnePole; + attackGain = (MY_FLOAT) 0.25; + loopGain = (MY_FLOAT) 0.25; + whichOne = 0; } Sampler :: ~Sampler() { - delete adsr; - delete filter; + delete adsr; + delete filter; } void Sampler :: keyOn() { - adsr->keyOn(); - attacks[0]->reset(); + adsr->keyOn(); + attacks[0]->reset(); } void Sampler :: keyOff() { - adsr->keyOff(); + adsr->keyOff(); } void Sampler :: noteOff(MY_FLOAT amplitude) { - this->keyOff(); + this->keyOff(); #if defined(_debug_) - printf("Sampler : NoteOff: Amp=%lf\n",amplitude); + printf("Sampler : NoteOff: Amp=%lf\n",amplitude); #endif } @@ -51,11 +51,11 @@ void Sampler :: setFreq(MY_FLOAT frequency) MY_FLOAT Sampler :: tick() { - lastOutput = attackGain * attacks[whichOne]->tick(); - lastOutput += loopGain * loops[whichOne]->tick(); - lastOutput = filter->tick(lastOutput); - lastOutput *= adsr->tick(); - return lastOutput; + lastOutput = attackGain * attacks[whichOne]->tick(); + lastOutput += loopGain * loops[whichOne]->tick(); + lastOutput = filter->tick(lastOutput); + lastOutput *= adsr->tick(); + return lastOutput; } void Sampler :: controlChange(int number, MY_FLOAT value) diff --git a/STK/Sampler.h b/STK/Sampler.h new file mode 100644 index 0000000..b60ae79 --- /dev/null +++ b/STK/Sampler.h @@ -0,0 +1,42 @@ +/*******************************************/ +/* Master Class for Sampling Synthesizer */ +/* by Perry R. Cook, 1995-96 */ +/* This instrument contains up to 5 */ +/* attack waves, 5 looped waves, and */ +/* an ADSR envelope. */ +/*******************************************/ + +#if !defined(__Sampler_h) +#define __Sampler_h + +#include "Instrmnt.h" +#include "ADSR.h" +#include "RawWvIn.h" +#include "OnePole.h" + +class Sampler : public Instrmnt +{ + protected: + ADSR *adsr; + RawWvIn *attacks[5]; + RawWvIn *loops[5]; + OnePole *filter; + MY_FLOAT baseFreq; + MY_FLOAT attackRatios[5]; + MY_FLOAT loopRatios[5]; + MY_FLOAT attackGain; + MY_FLOAT loopGain; + int whichOne; + public: + Sampler(); + virtual ~Sampler(); + void clear(); + virtual void setFreq(MY_FLOAT frequency); + void keyOn(); + void keyOff(); + virtual void noteOff(MY_FLOAT amplitude); + virtual MY_FLOAT tick(); + virtual void controlChange(int number, MY_FLOAT value); +}; + +#endif diff --git a/STK/Shakers.cpp b/STK/Shakers.cpp new file mode 100644 index 0000000..9c288b3 --- /dev/null +++ b/STK/Shakers.cpp @@ -0,0 +1,737 @@ +/**********************************************************/ +/* PhISEM (Physically Informed Stochastic Event Modeling */ +/* by Perry R. Cook, Princeton, February 1997 */ +/* */ +/* Meta-model that simulates all of: */ +/* Maraca Simulation by Perry R. Cook, Princeton, 1996-7 */ +/* Sekere Simulation by Perry R. Cook, Princeton, 1996-7 */ +/* Cabasa Simulation by Perry R. Cook, Princeton, 1996-7 */ +/* Bamboo Windchime Simulation, by Perry R. Cook, 1996-7 */ +/* Water Drops Simulation, by Perry R. Cook, 1996-7 */ +/* Tambourine Simulation, by Perry R. Cook, 1996-7 */ +/* Sleighbells Simulation, by Perry R. Cook, 1996-7 */ +/* Guiro Simulation, by Perry R. Cook, 1996-7 */ +/* */ +/**********************************************************/ +/* PhOLIES (Physically-Oriented Library of */ +/* Imitated Environmental Sounds), Perry Cook, 1997-9 */ +/* */ +/* Stix1 (walking on brittle sticks) */ +/* Crunch1 (like new fallen snow, or not) */ +/* Wrench (basic socket wrench, friend of guiro) */ +/* Sandpapr (sandpaper) */ +/**********************************************************/ + +#include "Object.h" +#ifdef __OS_NeXT_ + #include +#endif + +int my_random(int max) // Return Random Int Between 0 and max +{ + long temp; +#if defined(__OS_Win_) /* For Windoze */ + temp = (long) rand(); +#else /* This is for unix */ + temp = random() >> 16; +#endif + temp = temp * (long) max; + temp = temp >> 15; + return (int) temp; +} + +MY_FLOAT float_random(MY_FLOAT max) // Return random float between 0.0 and max +{ + MY_FLOAT temp; + temp = (MY_FLOAT) my_random(32767); + temp = temp * 0.0000305185; + temp = temp * max; + return temp; +} + +MY_FLOAT noise_tick() // Return random MY_FLOAT float between -1.0 and 1.0 +{ + MY_FLOAT temp; + temp = my_random(32767) - 16384; + temp *= 0.0000610352; + return temp; +} + +/************************* MARACA *****************************/ +#define MARA_SOUND_DECAY 0.95 +#define MARA_SYSTEM_DECAY 0.999 +#define MARA_GAIN 25.0 +#define MARA_NUM_BEANS 25 +#define MARA_CENTER_FREQ 3200.0 +#define MARA_RESON 0.96 +/*********************** SEKERE *****************************/ +#define SEKE_SOUND_DECAY 0.96 +#define SEKE_SYSTEM_DECAY 0.999 +#define SEKE_GAIN 30.0 +#define SEKE_NUM_BEANS 64 +#define SEKE_CENTER_FREQ 5500.0 +#define SEKE_RESON 0.6 +/*********************** SANDPAPER **************************/ +#define SANDPAPR_SOUND_DECAY 0.999 +#define SANDPAPR_SYSTEM_DECAY 0.999 +#define SANDPAPR_GAIN 1.0 +#define SANDPAPR_NUM_GRAINS 128 +#define SANDPAPR_CENTER_FREQ 4500.0 +#define SANDPAPR_RESON 0.6 +/************************ CABASA *****************************/ +#define CABA_SOUND_DECAY 0.96 +#define CABA_SYSTEM_DECAY 0.997 +#define CABA_GAIN 150.0 +#define CABA_NUM_BEADS 512 +#define CABA_CENTER_FREQ 3000.0 +#define CABA_RESON 0.7 +/************************ Bamboo Wind Chimes *****************/ +#define BAMB_SOUND_DECAY 0.95 +#define BAMB_SYSTEM_DECAY 0.99995 +#define BAMB_GAIN 2.0 +#define BAMB_NUM_TUBES 1.25 +#define BAMB_CENTER_FREQ0 2800.0 +#define BAMB_CENTER_FREQ1 0.8 * 2800.0 +#define BAMB_CENTER_FREQ2 1.2 * 2800.0 +#define BAMB_RESON 0.995 +/******************* Water Drops ****************************/ +#define WUTR_SOUND_DECAY 0.95 +#define WUTR_SYSTEM_DECAY 0.999 +#define WUTR_GAIN 1.0 +#define WUTR_NUM_SOURCES 4 +#define WUTR_CENTER_FREQ0 450.0 +#define WUTR_CENTER_FREQ1 600.0 +#define WUTR_CENTER_FREQ2 750.0 +#define WUTR_RESON 0.9985 +#define WUTR_FREQ_SWEEP 1.0001 +/****************** TAMBOURINE *****************************/ +#define TAMB_SOUND_DECAY 0.95 +#define TAMB_SYSTEM_DECAY 0.9985 +#define TAMB_GAIN 10.0 +#define TAMB_NUM_TIMBRELS 32 +#define TAMB_SHELL_FREQ 2300 +#define TAMB_SHELL_GAIN 0.1 +#define TAMB_SHELL_RESON 0.96 +#define TAMB_CYMB_FREQ1 5600 +#define TAMB_CYMB_FREQ2 8100 +#define TAMB_CYMB_RESON 0.99 +/********************** SLEIGHBELLS *************************/ +#define SLEI_SOUND_DECAY 0.97 +#define SLEI_SYSTEM_DECAY 0.9994 +#define SLEI_GAIN 2.0 +#define SLEI_NUM_BELLS 32 +#define SLEI_CYMB_FREQ0 2500 +#define SLEI_CYMB_FREQ1 5300 +#define SLEI_CYMB_FREQ2 6500 +#define SLEI_CYMB_FREQ3 8300 +#define SLEI_CYMB_FREQ4 9800 +#define SLEI_CYMB_RESON 0.99 +/*************************** GUIRO ***********************/ +#define GUIR_SOUND_DECAY 0.95 +#define GUIR_GAIN 10.0 +#define GUIR_NUM_PARTS 128 +#define GUIR_GOURD_FREQ 2500.0 +#define GUIR_GOURD_RESON 0.97 +#define GUIR_GOURD_FREQ2 4000.0 +#define GUIR_GOURD_RESON2 0.97 +/************************** WRENCH ***********************/ +#define WRENCH_SOUND_DECAY 0.95 +#define WRENCH_GAIN 5 +#define WRENCH_NUM_PARTS 128 +#define WRENCH_FREQ 3200.0 +#define WRENCH_RESON 0.99 +#define WRENCH_FREQ2 8000.0 +#define WRENCH_RESON2 0.992 +/************************ COKECAN **************************/ +#define COKECAN_SOUND_DECAY 0.97 +#define COKECAN_SYSTEM_DECAY 0.999 +#define COKECAN_GAIN 1.0 +#define COKECAN_NUM_PARTS 48 +#define COKECAN_HELMFREQ 370 +#define COKECAN_HELM_RES 0.99 +#define COKECAN_METLFREQ0 1025 +#define COKECAN_METLFREQ1 1424 +#define COKECAN_METLFREQ2 2149 +#define COKECAN_METLFREQ3 3596 +#define COKECAN_METL_RES 0.992 +/************************************************************/ +/* PhOLIES (Physically-Oriented Library of */ +/* Imitated Environmental Sounds), Perry Cook, 1997-8 */ +/************************************************************/ + +/*********************** STIX1 *****************************/ +#define STIX1_SOUND_DECAY 0.96 +#define STIX1_SYSTEM_DECAY 0.998 +#define STIX1_GAIN 30.0 +#define STIX1_NUM_BEANS 2 +#define STIX1_CENTER_FREQ 5500.0 +#define STIX1_RESON 0.6 +/************************ Crunch1 ***************************/ +#define CRUNCH1_SOUND_DECAY 0.95 +#define CRUNCH1_SYSTEM_DECAY 0.99806 +#define CRUNCH1_GAIN 30.0 +#define CRUNCH1_NUM_BEADS 7 +#define CRUNCH1_CENTER_FREQ 800.0 +#define CRUNCH1_RESON 0.95 + +/************ THE ACTUAL CLASS ITSELF *********************/ + +#include "Shakers.h" +#include "SKINI11.msg" + +Shakers :: Shakers() : Instrmnt() +{ + int i; + + instType = 0; + shakeEnergy = 0.0; + num_freqs = 0; + sndLevel = 0.0; + + for (i=0;isetupNum(instType); +} + +Shakers :: ~Shakers() +{ +} + +#define MAX_SHAKE 2000.0 + +#define NUM_INST 13 + +char instrs[NUM_INST][16] = + {"Maraca", "Cabasa", "Sekere", "Guiro", + "Waterdrp", "Bamboo", "Tambourn", "Sleighbl", + "Stix1", "Crunch1", "Wrench", "SandPapr", "CokeCan"}; + +int Shakers :: setupName(char* instr) +{ + int i, which = 0; + + for (i=0;isetupNum(which); +} + +void Shakers :: setFinalZs(MY_FLOAT z0, MY_FLOAT z1, MY_FLOAT z2) { + finalZCoeffs[0] = z0; + finalZCoeffs[1] = z1; + finalZCoeffs[2] = z2; +} + +void Shakers :: setDecays(MY_FLOAT sndDecay, MY_FLOAT sysDecay) { + soundDecay = sndDecay; + systemDecay = sysDecay; +} + +int Shakers :: setFreqAndReson(int which, MY_FLOAT freq, MY_FLOAT reson) { + if (which < MAX_FREQS) { + resons[which] = reson; + center_freqs[which] = freq; + t_center_freqs[which] = freq; + coeffs[which][1] = reson * reson; + coeffs[which][0] = -reson * 2.0 * cos(freq * TWO_PI / SRATE); + return 1; + } + else return 0; +} + +int Shakers :: setupNum(int inst) +{ + int i; + MY_FLOAT temp; + + if (inst==1) { // cabasa_setup(); + num_objects = CABA_NUM_BEADS; + setDecays(CABA_SOUND_DECAY, CABA_SYSTEM_DECAY); + num_freqs = 1; + baseGain = CABA_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0] = temp; + freqalloc[0] = 0; + setFreqAndReson(0,CABA_CENTER_FREQ,CABA_RESON); + setFinalZs(1.0,-1.0,0.0); + } + else if (inst==2) { // sekere_setup(); + num_objects = SEKE_NUM_BEANS; + this->setDecays(SEKE_SOUND_DECAY,SEKE_SYSTEM_DECAY); + num_freqs = 1; + baseGain = SEKE_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0] = temp; + freqalloc[0] = 0; + this->setFreqAndReson(0,SEKE_CENTER_FREQ,SEKE_RESON); + this->setFinalZs(1.0, 0.0, -1.0); + } + else if (inst==3) { // guiro_setup(); + num_objects = GUIR_NUM_PARTS; + setDecays(GUIR_SOUND_DECAY,1.0); + num_freqs = 2; + baseGain = GUIR_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0]=temp; + gains[1]=temp; + freqalloc[0] = 0; + freqalloc[1] = 0; + freq_rand[0] = 0.0; + freq_rand[1] = 0.0; + setFreqAndReson(0,GUIR_GOURD_FREQ,GUIR_GOURD_RESON); + setFreqAndReson(1,GUIR_GOURD_FREQ2,GUIR_GOURD_RESON2); + ratchet = 0; + ratchetPos = 10; + } + else if (inst==4) { // wuter_setup(); + num_objects = WUTR_NUM_SOURCES; + setDecays(WUTR_SOUND_DECAY,WUTR_SYSTEM_DECAY); + num_freqs = 3; + baseGain = WUTR_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0]=temp; + gains[1]=temp; + gains[2]=temp; + freqalloc[0] = 1; + freqalloc[1] = 1; + freqalloc[2] = 1; + freq_rand[0] = 0.2; + freq_rand[1] = 0.2; + freq_rand[2] = 0.2; + setFreqAndReson(0,WUTR_CENTER_FREQ0,WUTR_RESON); + setFreqAndReson(1,WUTR_CENTER_FREQ0,WUTR_RESON); + setFreqAndReson(2,WUTR_CENTER_FREQ0,WUTR_RESON); + setFinalZs(1.0,0.0,0.0); + } + else if (inst==5) { // bamboo_setup(); + num_objects = BAMB_NUM_TUBES; + setDecays(BAMB_SOUND_DECAY, BAMB_SYSTEM_DECAY); + num_freqs = 3; + baseGain = BAMB_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0]=temp; + gains[1]=temp; + gains[2]=temp; + freqalloc[0] = 1; + freqalloc[1] = 1; + freqalloc[2] = 1; + freq_rand[0] = 0.2; + freq_rand[1] = 0.2; + freq_rand[2] = 0.2; + setFreqAndReson(0,BAMB_CENTER_FREQ0,BAMB_RESON); + setFreqAndReson(1,BAMB_CENTER_FREQ1,BAMB_RESON); + setFreqAndReson(2,BAMB_CENTER_FREQ2,BAMB_RESON); + setFinalZs(1.0,0.0,0.0); + } + else if (inst==6) { // tambourine_setup(); + num_objects = TAMB_NUM_TIMBRELS; + setDecays(TAMB_SOUND_DECAY,TAMB_SYSTEM_DECAY); + num_freqs = 3; + baseGain = TAMB_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0]=temp*TAMB_SHELL_GAIN; + gains[1]=temp*0.8; + gains[2]=temp; + freqalloc[0] = 0; + freqalloc[1] = 1; + freqalloc[2] = 1; + freq_rand[0] = 0.0; + freq_rand[1] = 0.05; + freq_rand[2] = 0.05; + setFreqAndReson(0,TAMB_SHELL_FREQ,TAMB_SHELL_RESON); + setFreqAndReson(1,TAMB_CYMB_FREQ1,TAMB_CYMB_RESON); + setFreqAndReson(2,TAMB_CYMB_FREQ2,TAMB_CYMB_RESON); + setFinalZs(1.0,0.0,-1.0); + } + else if (inst==7) { // sleighbell_setup(); + num_objects = SLEI_NUM_BELLS; + setDecays(SLEI_SOUND_DECAY,SLEI_SYSTEM_DECAY); + num_freqs = 5; + baseGain = SLEI_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0]=temp; + gains[1]=temp; + gains[2]=temp; + gains[3]=temp*0.5; + gains[4]=temp*0.3; + for (i=0;isetDecays(SANDPAPR_SOUND_DECAY,SANDPAPR_SYSTEM_DECAY); + num_freqs = 1; + baseGain = SANDPAPR_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0] = temp; + freqalloc[0] = 0; + this->setFreqAndReson(0,SANDPAPR_CENTER_FREQ,SANDPAPR_RESON); + this->setFinalZs(1.0, 0.0, -1.0); + } + else if (inst==12) { // cokecan_setup(); + num_objects = COKECAN_NUM_PARTS; + setDecays(COKECAN_SOUND_DECAY,COKECAN_SYSTEM_DECAY); + num_freqs = 5; + baseGain = COKECAN_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0]=temp; + gains[1]=temp*1.8; + gains[2]=temp*1.8; + gains[3]=temp*1.8; + gains[4]=temp*1.8; + freqalloc[0] = 0; + freqalloc[1] = 0; + freqalloc[2] = 0; + freqalloc[3] = 0; + freqalloc[4] = 0; + setFreqAndReson(0,COKECAN_HELMFREQ,COKECAN_HELM_RES); + setFreqAndReson(1,COKECAN_METLFREQ0,COKECAN_METL_RES); + setFreqAndReson(2,COKECAN_METLFREQ1,COKECAN_METL_RES); + setFreqAndReson(3,COKECAN_METLFREQ2,COKECAN_METL_RES); + setFreqAndReson(4,COKECAN_METLFREQ3,COKECAN_METL_RES); + setFinalZs(1.0,0.0,-1.0); + } + else { // maraca_setup(); inst == 0 or other + num_objects = MARA_NUM_BEANS; + setDecays(MARA_SOUND_DECAY,MARA_SYSTEM_DECAY); + num_freqs = 1; + baseGain = MARA_GAIN; + temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + gains[0]=temp; + freqalloc[0] = 0; + setFreqAndReson(0,MARA_CENTER_FREQ,MARA_RESON); + setFinalZs(1.0,-1.0,0.0); + } + return inst; +} + +void Shakers :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + // Yep ... pretty kludgey, but it works! + int noteNum = (int) ((12*log(freq/220)/log(2)) + 57.01) % 32; + if (instType != noteNum) instType = this->setupNum(noteNum); + shakeEnergy = amp * MAX_SHAKE * 0.1; + if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE; + if (instType==10 || instType==3) ratchetPos += 1; +#if defined(_debug_) + printf("Shakers : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} + +void Shakers :: noteOff(MY_FLOAT amp) +{ + shakeEnergy = 0.0; +} + +#define MIN_ENERGY 0.3 + +MY_FLOAT Shakers :: tick() +{ + MY_FLOAT data; + MY_FLOAT temp_rand; + int i; + + if (instType==4) { + if (shakeEnergy > MIN_ENERGY) { + lastOutput = wuter_tick(); + } + else { + lastOutput = 0.0; + } + } + else if (instType==10 || instType==3) { + if (ratchetPos > 0) { + ratchet -= (ratchetDelta + (0.002*totalEnergy)); + if (ratchet < 0.0) { + ratchet = 1.0; + ratchetPos -= 1; + } + totalEnergy = ratchet; + lastOutput = ratchet_tick(); + } + else lastOutput = 0.0; + } + else { + // MY_FLOAT generic_tick() { + if (shakeEnergy > MIN_ENERGY) { + shakeEnergy *= systemDecay; // Exponential system decay + if (float_random(1024.0) < num_objects) { + sndLevel += shakeEnergy; + for (i=0;i 10000.0) data = 10000.0; + if (data < -10000.0) data = -10000.0; + lastOutput = data; + } + else lastOutput = 0.0; + } + + lastOutput *= 0.0001; + + return lastOutput; +} + +void Shakers :: controlChange(int number, MY_FLOAT value) +{ + MY_FLOAT temp; + int i; +#if defined(_debug_) + printf("Shakers : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_Breath_) { // control_change #2 +#if defined(_debug_) + printf("shaking \n"); +#endif + shakeEnergy += value * NORM_7 * MAX_SHAKE * 0.1; + if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE; + if (instType==10 || instType==3) { + ratchetPos = (int) fabs(value - lastRatchetPos); + ratchetDelta = 0.0002 * ratchetPos; + lastRatchetPos = (int) value; + } + } + else if (number == __SK_FootControl_) { // control_change #4 +#if defined(_debug_) + printf("setting decay\n"); +#endif + systemDecay = 0.998 + (value * NORM_7 * 0.002); + } + else if (number == __SK_ModFrequency_) { // control_change #11 +#if defined(_debug_) + printf("setting number of objects\n"); +#endif + if (instType==5) num_objects = (MY_FLOAT) (value/32.0) + 1; + else num_objects = (MY_FLOAT) value + 1; + gains[0] = log(num_objects) * baseGain / (MY_FLOAT) num_objects; + } + else if (number == __SK_ModWheel_) { // control_change #1 + for (i=0;i MAX_SHAKE) shakeEnergy = MAX_SHAKE; + if (instType==10 || instType==3) { + ratchetPos = (int) fabs(value - lastRatchetPos); + ratchetDelta = 0.0002 * ratchetPos; + lastRatchetPos = (int) value; + } + } + else if (number == __SK_ShakerInst_) { // control_change #1071 + instType = (int) (value + 0.5); // Just to be safe + this->setupNum(instType); + } + else { + printf("Shakers : Undefined Control Number!!\n"); + } +} + +/*********************************************************/ +/**************** KLUDGE-O-MATIC-O-RAMA **************/ + +MY_FLOAT Shakers :: wuter_tick() { + MY_FLOAT data; + int j; + shakeEnergy *= systemDecay; // Exponential system decay + if (my_random(32767) < num_objects) { + sndLevel = shakeEnergy; + j = my_random(3); + if (j == 0) { + center_freqs[0] = WUTR_CENTER_FREQ1 * (0.75 + (0.25 * noise_tick())); + gains[0] = fabs(noise_tick()); + } + else if (j == 1) { + center_freqs[1] = WUTR_CENTER_FREQ1 * (1.0 + (0.25 * noise_tick())); + gains[1] = fabs(noise_tick()); + } + else { + center_freqs[2] = WUTR_CENTER_FREQ1 * (1.25 + (0.25 * noise_tick())); + gains[2] = fabs(noise_tick()); + } + } + + gains[0] *= resons[0]; + if (gains[0] > 0.001) { + center_freqs[0] *= WUTR_FREQ_SWEEP; + coeffs[0][0] = -resons[0] * 2.0 * + cos(center_freqs[0] * TWO_PI / SRATE); + } + gains[1] *= resons[1]; + if (gains[1] > 0.001) { + center_freqs[1] *= WUTR_FREQ_SWEEP; + coeffs[1][0] = -resons[1] * 2.0 * + cos(center_freqs[1] * TWO_PI / SRATE); + } + gains[2] *= resons[2]; + if (gains[2] > 0.001) { + center_freqs[2] *= WUTR_FREQ_SWEEP; + coeffs[2][0] = -resons[2] * 2.0 * + cos(center_freqs[2] * TWO_PI / SRATE); + } + + sndLevel *= soundDecay; // Each (all) event(s) + // decay(s) exponentially + inputs[0] = sndLevel; + inputs[0] *= noise_tick(); // Actual Sound is Random + inputs[1] = inputs[0] * gains[1]; + inputs[2] = inputs[0] * gains[2]; + inputs[0] *= gains[0]; + inputs[0] -= outputs[0][0]*coeffs[0][0]; + inputs[0] -= outputs[0][1]*coeffs[0][1]; + outputs[0][1] = outputs[0][0]; + outputs[0][0] = inputs[0]; + data = gains[0]*outputs[0][0]; + inputs[1] -= outputs[1][0]*coeffs[1][0]; + inputs[1] -= outputs[1][1]*coeffs[1][1]; + outputs[1][1] = outputs[1][0]; + outputs[1][0] = inputs[1]; + data += gains[1]*outputs[1][0]; + inputs[2] -= outputs[2][0]*coeffs[2][0]; + inputs[2] -= outputs[2][1]*coeffs[2][1]; + outputs[2][1] = outputs[2][0]; + outputs[2][0] = inputs[2]; + data += gains[2]*outputs[2][0]; + + finalZ[2] = finalZ[1]; + finalZ[1] = finalZ[0]; + finalZ[0] = data * 4; + + data = finalZ[2] - finalZ[0]; + return data; +} + +MY_FLOAT Shakers :: ratchet_tick() { + MY_FLOAT data; + if (my_random(1024) < num_objects) { + sndLevel += 512 * ratchet * totalEnergy; + } + inputs[0] = sndLevel; + inputs[0] *= noise_tick() * ratchet; + sndLevel *= soundDecay; + + inputs[1] = inputs[0]; + inputs[0] -= outputs[0][0]*coeffs[0][0]; + inputs[0] -= outputs[0][1]*coeffs[0][1]; + outputs[0][1] = outputs[0][0]; + outputs[0][0] = inputs[0]; + inputs[1] -= outputs[1][0]*coeffs[1][0]; + inputs[1] -= outputs[1][1]*coeffs[1][1]; + outputs[1][1] = outputs[1][0]; + outputs[1][0] = inputs[1]; + + finalZ[2] = finalZ[1]; + finalZ[1] = finalZ[0]; + finalZ[0] = gains[0]*outputs[0][1] + gains[1]*outputs[1][1]; + data = finalZ[0] - finalZ[2]; + return data; +} + diff --git a/STK/Shakers.h b/STK/Shakers.h new file mode 100644 index 0000000..54b5770 --- /dev/null +++ b/STK/Shakers.h @@ -0,0 +1,73 @@ +/**********************************************************/ +/* PhISEM (Physically Informed Stochastic Event Modeling */ +/* by Perry R. Cook, Princeton, February 1997 */ +/* */ +/* Meta-model that simulates all of: */ +/* Maraca Simulation by Perry R. Cook, Princeton, 1996-7 */ +/* Sekere Simulation by Perry R. Cook, Princeton, 1996-7 */ +/* Cabasa Simulation by Perry R. Cook, Princeton, 1996-7 */ +/* Bamboo Windchime Simulation, by Perry R. Cook, 1996-7 */ +/* Water Drops Simulation, by Perry R. Cook, 1996-7 */ +/* Tambourine Simulation, by Perry R. Cook, 1996-7 */ +/* Sleighbells Simulation, by Perry R. Cook, 1996-7 */ +/* Guiro Simulation, by Perry R. Cook, 1996-7 */ +/* */ +/**********************************************************/ +/* PhOLIES (Physically-Oriented Library of */ +/* Imitated Environmental Sounds), Perry Cook, 1997-9 */ +/* Stix1 (walking on brittle sticks) */ +/* Crunch1 (like new fallen snow, or not) */ +/* Wrench (basic socket wrench, friend of guiro) */ +/* Sandpapr (sandpaper) */ +/**********************************************************/ + +#if !defined(__Shakers_h) +#define __Shakers_h + +#include "Instrmnt.h" + +#define MAX_FREQS 8 + +class Shakers : public Instrmnt +{ + protected: + int instType; + int setFreqAndReson(int which, MY_FLOAT freq, MY_FLOAT reson); + int ratchetPos, lastRatchetPos; + MY_FLOAT shakeEnergy; + MY_FLOAT inputs[MAX_FREQS]; + MY_FLOAT outputs[MAX_FREQS][2]; + MY_FLOAT coeffs[MAX_FREQS][2]; + MY_FLOAT sndLevel; + MY_FLOAT baseGain; + MY_FLOAT gains[MAX_FREQS]; + int num_freqs; + MY_FLOAT t_center_freqs[MAX_FREQS]; + MY_FLOAT center_freqs[MAX_FREQS]; + MY_FLOAT resons[MAX_FREQS]; + MY_FLOAT freq_rand[MAX_FREQS]; + int freqalloc[MAX_FREQS]; + MY_FLOAT soundDecay; + MY_FLOAT systemDecay; + MY_FLOAT num_objects; + MY_FLOAT collLikely; + MY_FLOAT totalEnergy; + MY_FLOAT ratchet,ratchetDelta; + MY_FLOAT finalZ[3]; + MY_FLOAT finalZCoeffs[3]; + void setDecays(MY_FLOAT sndDecay, MY_FLOAT sysDecay); + void setFinalZs(MY_FLOAT z0, MY_FLOAT z1, MY_FLOAT z2); + MY_FLOAT wuter_tick(); + MY_FLOAT ratchet_tick(); + public: + Shakers(); + ~Shakers(); + int setupName(char* instr); + int setupNum(int inst); + virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); + virtual void noteOff(MY_FLOAT amp); + MY_FLOAT tick(); + virtual void controlChange(int number, MY_FLOAT value); +}; + +#endif diff --git a/STK/Simple.cpp b/STK/Simple.cpp new file mode 100644 index 0000000..d415dff --- /dev/null +++ b/STK/Simple.cpp @@ -0,0 +1,115 @@ +/*******************************************/ +/* Master Class for Simple Instrument */ +/* by Perry R. Cook, 1995-96 */ +/* This instrument contains 1 looped */ +/* wave, 1 noise source, 1 biquad filter */ +/* 1 one-pole filter, and 1 ADSR envelope */ +/*******************************************/ + +#include "Simple.h" + +Simple :: Simple() +{ + MY_FLOAT coeffs[2]; + adsr = new ADSR; + baseFreq = (MY_FLOAT) 440.0; + + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + loop = new RawWvIn(strcat(file,"rawwaves/impuls10.raw"),"looping"); + loop->normalize(); + filter = new OnePole; + noise = new Noise; + bqpoles = new TwoPole; + bqzeroes = new TwoZero; + coeffs[0] = 0; + coeffs[1] = -1; + bqzeroes->setZeroCoeffs(coeffs); + filter->setPole(0.5); + this->setFreq(baseFreq); + loopGain = 0.5; +} + +Simple :: ~Simple() +{ + delete adsr; + delete loop; + delete filter; + delete bqzeroes; + delete bqpoles; +} + +void Simple :: keyOn() +{ + adsr->keyOn(); +} + +void Simple :: keyOff() +{ + adsr->keyOff(); +} + +void Simple :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + this->keyOn(); + this->setFreq(freq); + filter->setGain(amp); +#if defined(_debug_) + printf("Simple : NoteOn: Freq= %lf, Amp=%lf\n",freq, amp); +#endif +} +void Simple :: noteOff(MY_FLOAT amplitude) +{ + this->keyOff(); +#if defined(_debug_) + printf("Simple : NoteOff: Amp=%lf\n",amplitude); +#endif +} + +void Simple :: setFreq(MY_FLOAT frequency) +{ +#define R 0.98 + + MY_FLOAT coeffs[2]; + coeffs[0] = 2 * R * cos(TWO_PI * ONE_OVER_SRATE * frequency); + coeffs[1] = - R * R; + bqpoles->setPoleCoeffs(coeffs); + bqpoles->setGain(1.0 - R); + loop->setFreq(frequency); +} + +MY_FLOAT Simple :: tick() +{ + lastOutput = loopGain * loop->tick(); + bqzeroes->tick(bqpoles->tick(noise->tick())); + lastOutput += (1.0 - loopGain) * bqzeroes->lastOut(); + lastOutput = filter->tick(lastOutput); + lastOutput *= adsr->tick(); + return lastOutput; +} + +#include "SKINI11.msg" + +void Simple :: controlChange(int number, MY_FLOAT value) +{ +#if defined(_debug_) + printf("Simple : ControlChange: Number=%i Value=%f\n",number,value); +#endif + if (number == __SK_Breath_) + filter->setPole(0.99 * (1.0 - (value * NORM_7 * 2.0))); + else if (number == __SK_NoiseLevel_) + loopGain = 1.0 - (NORM_7 * value); + else if (number == __SK_ModFrequency_) { + adsr->setAttackRate(value * NORM_7); + adsr->setDecayRate(value * NORM_7); + adsr->setReleaseRate(value * NORM_7); + } + else if (number == __SK_ModWheel_) + printf("Mod Wheel Unimplemented\n"); + else if (number == __SK_AfterTouch_Cont_) + adsr->setTarget(value * NORM_7); + else { + printf("Simple : Undefined Control Number!!\n"); + } +} diff --git a/STK/Simple.h b/STK/Simple.h new file mode 100644 index 0000000..7da9bfc --- /dev/null +++ b/STK/Simple.h @@ -0,0 +1,44 @@ +/*******************************************/ +/* Master Class for Simple Instrument */ +/* by Perry R. Cook, 1995-96 */ +/* This instrument contains 1 looped */ +/* wave, 1 noise source, 1 biquad filter */ +/* 1 one-pole filter, and 1 ADSR envelope */ +/*******************************************/ + +#if !defined(__Simple_h) +#define __Simple_h + +#include "Instrmnt.h" +#include "ADSR.h" +#include "RawWvIn.h" +#include "OnePole.h" +#include "TwoPole.h" +#include "TwoZero.h" +#include "Noise.h" + +class Simple : public Instrmnt +{ + protected: + ADSR *adsr; + RawWvIn *loop; + OnePole *filter; + TwoPole *bqpoles; + TwoZero *bqzeroes; + Noise *noise; + MY_FLOAT baseFreq; + MY_FLOAT loopGain; + public: + Simple(); + ~Simple(); + void clear(); + virtual void setFreq(MY_FLOAT frequency); + void keyOn(); + void keyOff(); + virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); + virtual void noteOff(MY_FLOAT amplitude); + virtual MY_FLOAT tick(); + virtual void controlChange(int number, MY_FLOAT value); +}; + +#endif diff --git a/SingWave.cpp b/STK/SingWave.cpp similarity index 97% rename from SingWave.cpp rename to STK/SingWave.cpp index 9109e0e..b8968be 100644 --- a/SingWave.cpp +++ b/STK/SingWave.cpp @@ -16,9 +16,9 @@ SingWave :: SingWave(char *fileName) { long i; - short temp; + INT16 temp; FILE *fd; - // extern short SwapShort(short); + // extern short SwapINT16(short); fd = fopen(fileName,"rb"); if (!fd) { @@ -28,11 +28,11 @@ SingWave :: SingWave(char *fileName) while (fread(&temp,2,1,fd)) i++; length = i; fseek(fd,0,0); - data = (MY_FLOAT *) malloc(MY_FLOAT_SIZE * (length + 1)); + data = (MY_FLOAT *) malloc((length + 1) * sizeof(MY_FLOAT)); i = 0; while (fread(&temp,2,1,fd)) { #ifdef __LITTLE_ENDIAN__ - temp = SwapShort(temp); + temp = SwapINT16(temp); #endif data[i] = temp; i++; diff --git a/SingWave.h b/STK/SingWave.h similarity index 100% rename from SingWave.h rename to STK/SingWave.h diff --git a/STK/SndWvIn.cpp b/STK/SndWvIn.cpp new file mode 100644 index 0000000..34ff51d --- /dev/null +++ b/STK/SndWvIn.cpp @@ -0,0 +1,102 @@ +/*******************************************/ +/* SndWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to open NeXT/Sun .snd 16-bit data */ +/* (signed integer) files for playback. */ +/* */ +/* .snd files are always big-endian. */ +/*******************************************/ + +#include "SndWvIn.h" + +#ifdef __LITTLE_ENDIAN__ + #include "swapstuf.h" +#endif + +SndWvIn :: SndWvIn(char *fileName, char *mode) +{ + // Open the file and get header info + FILE *fd; + fd = fopen(fileName,"rb"); + if (!fd) { + printf("Couldn't open or find .snd file %s !!!\n",fileName); + exit(0); + } + + // Make sure this is a .snd format file + char magic[4]; + fseek(fd,0,SEEK_SET); // Locate magic number in header + fread(magic,4,1,fd); + if (strncmp(magic,".snd",4)) { + printf("This doesn't appear to be a .snd file %s !!!\n",fileName); + exit(0); + } + + // Get number of channels from the header + INT32 int32temp; + fseek(fd,20,SEEK_SET); + fread(&int32temp,4,1,fd); +#ifdef __LITTLE_ENDIAN__ + int32temp = SwapINT32(int32temp); +#endif + channels = int32temp; + + // Get length of data from the header + fseek(fd,8,SEEK_SET); + fread(&int32temp,4,1,fd); +#ifdef __LITTLE_ENDIAN__ + int32temp = SwapINT32(int32temp); +#endif + length = int32temp / 2 / channels; // channel length in 2-byte samples + data = (MY_FLOAT *) new MY_FLOAT[(length+1)*channels]; + + // Read samples into data[] + INT16 temp; + long i = 0; + fseek(fd,40,SEEK_SET); + while (fread(&temp,2,1,fd)) { +#ifdef __LITTLE_ENDIAN__ + temp = SwapINT16 (temp); +#endif + data[i++] = (MY_FLOAT) temp; + } + + // Get file sample rate from the header and set the default rate + fseek(fd,16,SEEK_SET); + fread(&int32temp,4,1,fd); +#ifdef __LITTLE_ENDIAN__ + int32temp = SwapINT32(int32temp); +#endif + rate = (MY_FLOAT) (int32temp/SRATE); + fclose(fd); + + // Setup for looping or one-shot playback + if (!strcmp(mode,"looping")) { + looping = 1; + for (int j=0; j 0.0) interpolate = 1; + else interpolate = 0; + time = (MY_FLOAT) 0.0; + phaseOffset = (MY_FLOAT) 0.0; + finished = 0; + lastOutput = (MY_FLOAT *) calloc(channels, sizeof(MY_FLOAT)); +} + +SndWvIn :: ~SndWvIn() +{ +} diff --git a/STK/SndWvIn.h b/STK/SndWvIn.h new file mode 100644 index 0000000..5884e2e --- /dev/null +++ b/STK/SndWvIn.h @@ -0,0 +1,25 @@ +/*******************************************/ +/* SndWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to open NeXT/Sun .snd 16-bit data */ +/* (signed integer) files for playback. */ +/* */ +/* .snd files are always big-endian. */ +/*******************************************/ + +#if !defined(__SndWvIn_h) +#define __SndWvIn_h + +#include "Object.h" +#include "WvIn.h" + +class SndWvIn : public WvIn +{ + public: + SndWvIn(char *fileName, char *mode); + ~SndWvIn(); +}; + +#endif diff --git a/STK/SndWvOut.cpp b/STK/SndWvOut.cpp new file mode 100644 index 0000000..3040898 --- /dev/null +++ b/STK/SndWvOut.cpp @@ -0,0 +1,133 @@ +/*******************************************/ +/* NeXT Soundfile Output Class */ +/* by Perry R. Cook, 1996 */ +/* revised by Gary P. Scavone, 1999 */ +/* */ +/* This one opens a NeXT .snd file, and */ +/* even knows how to byte-swap! */ +/*******************************************/ + +#include "SndWvOut.h" + +#ifdef __LITTLE_ENDIAN__ + #include "swapstuf.h" +#endif + +/******** NeXT Soundfile Header Struct *******/ +struct headerform { + char pref[4]; + INT32 hdr_length; + INT32 file_length; + INT32 mode; + INT32 samp_rate; + INT32 num_channels; + char comment[16]; +}; + +FILE *openNeXTFile(int chans,char *fileName) { + struct headerform hdr = {".sn",40,0,3,(INT32) SRATE,1,"Created by STK"}; + char tempName[128]; + FILE *fd; + + hdr.pref[3] = 'd'; + + strcpy(tempName,fileName); + if (strstr(tempName,".snd") == NULL) strcat(tempName,".snd"); + hdr.num_channels = chans; + fd = fopen(tempName,"wb"); + if (!fd) { + printf("Couldn't create soundfile %s !!!!!!!!\n",fileName); + exit(0); + } +#ifdef __LITTLE_ENDIAN__ + hdr.hdr_length = SwapINT32 (hdr.hdr_length); + hdr.file_length = SwapINT32 (hdr.file_length); + hdr.mode = SwapINT32 (hdr.mode); + hdr.samp_rate = SwapINT32 (hdr.samp_rate); + hdr.num_channels = SwapINT32 (hdr.num_channels); +#endif + printf("\nCreating soundfile %s\n\n", tempName); + fwrite(&hdr,4,10,fd); + return fd; +} + +SndWvOut :: SndWvOut(char *fileName) +{ + channels = 1; + fd = openNeXTFile(channels,fileName); + counter = 0; + totalCount = 0; +} + +SndWvOut :: SndWvOut(int chans, char *fileName) +{ + if (chans > 24) { + fprintf(stderr,"SndWvOut: Unsupported # of channels: %d\n", chans); + exit(0); + } + channels = chans; + fd = openNeXTFile(channels,fileName); + counter = 0; + totalCount = 0; +} + +SndWvOut :: ~SndWvOut() +{ + double temp; + + fwrite(data,2,counter,fd); + fseek(fd,8,SEEK_SET); + temp = (double) totalCount * ONE_OVER_SRATE; + printf("%f Seconds Computed\n",temp); + totalCount *= 2*channels; +#ifdef __LITTLE_ENDIAN__ + totalCount = SwapINT32 (totalCount); +#endif + fwrite(&totalCount,4,1,fd); + fclose(fd); +} + +long SndWvOut :: getCounter() +{ + return totalCount; +} + +MY_FLOAT SndWvOut :: getTime() +{ + return (MY_FLOAT) totalCount * ONE_OVER_SRATE; +} + +void SndWvOut :: tick(MY_FLOAT sample) +{ + INT16 isample; + + isample = (INT16) (sample * 32000.0); +#ifdef __LITTLE_ENDIAN__ + isample = SwapINT16 (isample); +#endif + for (int i=0;i +#include + +#ifdef __LITTLE_ENDIAN__ + #include "swapstuf.h" +#endif + +TablLook :: TablLook(char *fileName) +{ + extern double SwapDouble(double); + + // Use the system call "stat" to determine the file length + struct stat filestat; + if (stat(fileName, &filestat) == -1) + { /* Opening file failed */ + fprintf(stderr,"TablLook - Cannot access or find table lookup file: %s !!!\n",fileName); + exit(0); + } + length = (long) filestat.st_size / 8; // length in 8-byte samples + + // Open the file and read samples into data[] + FILE *fd; + fd = fopen(fileName,"rb"); + if (!fd) { + printf("TablLook - Couldn't open or find table lookup file %s !!!\n",fileName); + exit(0); + } + + data = (MY_FLOAT *) new MY_FLOAT[length]; + + // Read samples into data[] + long i = 0; + double temp; + while (fread(&temp,8,1,fd)) { +#ifdef __LITTLE_ENDIAN__ + temp = SwapDouble(temp); +#endif + data[i++] = (MY_FLOAT) temp; + } + fclose(fd); + + lastOutput = 0.0; +} + +TablLook :: ~TablLook() +{ + delete [ ] data; +} + +long TablLook :: getLength() +{ + return length; +} + +MY_FLOAT TablLook :: tick(MY_FLOAT index) +{ + static MY_FLOAT alpha; + static long temp; + + if (index > length-1) { + fprintf(stderr,"TablLook: Index (%f) exceeds table length ... sticking at end!\n", index); + index = length-1; + } + if (index < 0.0) { + fprintf(stderr,"TablLook: Index (%f) is less than zero ... setting to zero\n", index); + index = 0.0; + } + + // Index OK (in range 0 to length-1) + temp = (long) index; // Integer part of index + alpha = index - (MY_FLOAT) temp; // Fractional part of index + if (alpha > 0.0) { // Do linear interpolation + lastOutput = data[temp]; + lastOutput += (alpha*(data[temp+1] - lastOutput)); + } + else lastOutput = data[temp]; + + return lastOutput; +} + +MY_FLOAT TablLook :: lastOut() +{ + return lastOutput; +} diff --git a/STK/TablLook.h b/STK/TablLook.h new file mode 100644 index 0000000..d01891f --- /dev/null +++ b/STK/TablLook.h @@ -0,0 +1,31 @@ +/********************************************/ +/* Table Lookup Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This class loads a table of floating */ +/* point doubles, which are assumed to be */ +/* in big-endian format. Linear */ +/* interpolation is performed for */ +/* fractional lookup indexes. */ +/********************************************/ + +#include "Object.h" + +#if !defined(__TablLook_h) +#define __TablLook_h + +class TablLook : public Object +{ + protected: + long length; + MY_FLOAT *data; + MY_FLOAT lastOutput; + public: + TablLook(char *fileName); + ~TablLook(); + long getLength(); + MY_FLOAT tick(MY_FLOAT index); + MY_FLOAT lastOut(); +}; + +#endif // defined(__TablLook_h) diff --git a/STK/TubeBell.cpp b/STK/TubeBell.cpp new file mode 100644 index 0000000..65dc28d --- /dev/null +++ b/STK/TubeBell.cpp @@ -0,0 +1,62 @@ +/******************************************/ +/* Tubular Bell (Orch. Chime) Subclass */ +/* of Algorithm 5 (TX81Z) Subclass of */ +/* 4 Operator FM Synth */ +/* by Perry R. Cook, 1995-96 */ +/******************************************/ + +#include "TubeBell.h" + +TubeBell :: TubeBell() : FM4Alg5() +{ + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file1[128]; + char file2[128]; + char file3[128]; + char file4[128]; + strcpy(file1, RAWWAVE_PATH); + strcpy(file2, RAWWAVE_PATH); + strcpy(file3, RAWWAVE_PATH); + strcpy(file4, RAWWAVE_PATH); + this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"), + strcat(file2,"rawwaves/sinewave.raw"), + strcat(file3,"rawwaves/sinewave.raw"), + strcat(file4,"rawwaves/fwavblnk.raw")); + + this->setRatio(0,(MY_FLOAT) (1.0 * 0.995)); + this->setRatio(1,(MY_FLOAT) (1.414 * 0.995)); + this->setRatio(2,(MY_FLOAT) (1.0 * 1.005)); + this->setRatio(3,(MY_FLOAT) (1.414 * 1.000)); + gains[0] = __FM4Op_gains[94]; + gains[1] = __FM4Op_gains[76]; + gains[2] = __FM4Op_gains[99]; + gains[3] = __FM4Op_gains[71]; + adsr[0]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[1]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 2.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[3]->setAllTimes((MY_FLOAT) 0.004,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + twozero->setGain((MY_FLOAT) 0.5); + vibWave->setFreq((MY_FLOAT) 2.0); +} + +void TubeBell :: setFreq(MY_FLOAT frequency) +{ + baseFreq = frequency; + waves[0]->setFreq(baseFreq * ratios[0]); + waves[1]->setFreq(baseFreq * ratios[1]); + waves[2]->setFreq(baseFreq * ratios[2]); + waves[3]->setFreq(baseFreq * ratios[3]); +} + +void TubeBell :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + gains[0] = amp * __FM4Op_gains[94]; + gains[1] = amp * __FM4Op_gains[76]; + gains[2] = amp * __FM4Op_gains[99]; + gains[3] = amp * __FM4Op_gains[71]; + this->setFreq(freq); + this->keyOn(); +#if defined(_debug_) + printf("TubeBell : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} diff --git a/TubeBell.h b/STK/TubeBell.h similarity index 100% rename from TubeBell.h rename to STK/TubeBell.h diff --git a/STK/TwoPole.cpp b/STK/TwoPole.cpp new file mode 100644 index 0000000..1a0a1c4 --- /dev/null +++ b/STK/TwoPole.cpp @@ -0,0 +1,60 @@ +/*******************************************/ +/* Two Pole Filter Class, */ +/* by Perry R. Cook, 1995-96 */ +/* See books on filters to understand */ +/* more about how this works. Nothing */ +/* out of the ordinary in this version. */ +/*******************************************/ + +#include "TwoPole.h" + +TwoPole :: TwoPole() : Filter() +{ + outputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT)); + poleCoeffs[0] = (MY_FLOAT) 0.0; + poleCoeffs[1] = (MY_FLOAT) 0.0; + gain = (MY_FLOAT) 1.0; + this->clear(); +} + +TwoPole :: ~TwoPole() +{ + free(outputs); +} + +void TwoPole :: clear() +{ + outputs[0] = (MY_FLOAT) 0.0; + outputs[1] = (MY_FLOAT) 0.0; + lastOutput = (MY_FLOAT) 0.0; +} + +void TwoPole :: setPoleCoeffs(MY_FLOAT *coeffs) +{ + poleCoeffs[0] = coeffs[0]; + poleCoeffs[1] = coeffs[1]; +} + +void TwoPole :: setFreqAndReson(MY_FLOAT freq, MY_FLOAT reson) +{ + poleCoeffs[1] = - (reson * reson); + poleCoeffs[0] = (MY_FLOAT) 2.0 * reson * cos(TWO_PI * (double) freq / SRATE); +} + +void TwoPole :: setGain(MY_FLOAT aValue) +{ + gain = aValue; +} + +MY_FLOAT TwoPole :: tick(MY_FLOAT sample) // Perform Filter Operation +{ // TwoPole is a two pole filter (duh!) + MY_FLOAT temp; // Look it up in your favorite DSP text + temp = sample * gain; + temp += poleCoeffs[0] * outputs[0]; + temp += poleCoeffs[1] * outputs[1]; + outputs[1] = outputs[0]; + outputs[0] = temp; + lastOutput = outputs[0]; + return lastOutput; +} + diff --git a/TwoPole.h b/STK/TwoPole.h similarity index 100% rename from TwoPole.h rename to STK/TwoPole.h diff --git a/STK/TwoZero.cpp b/STK/TwoZero.cpp new file mode 100644 index 0000000..12dd193 --- /dev/null +++ b/STK/TwoZero.cpp @@ -0,0 +1,53 @@ +/*******************************************/ +/* Two Zero Filter Class, */ +/* by Perry R. Cook, 1995-96 */ +/* See books on filters to understand */ +/* more about how this works. Nothing */ +/* out of the ordinary in this version. */ +/*******************************************/ + +#include "TwoZero.h" + +TwoZero :: TwoZero() : Filter() +{ + inputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT)); + zeroCoeffs[0] = (MY_FLOAT) 0.0; + zeroCoeffs[1] = (MY_FLOAT) 0.0; + gain = (MY_FLOAT) 1.0; + this->clear(); +} + +TwoZero :: ~TwoZero() +{ + free(inputs); +} + +void TwoZero :: clear() +{ + inputs[0] = (MY_FLOAT) 0.0; + inputs[1] = (MY_FLOAT) 0.0; + lastOutput = (MY_FLOAT) 0.0; +} + +void TwoZero :: setZeroCoeffs(MY_FLOAT *coeffs) +{ + zeroCoeffs[0] = coeffs[0]; + zeroCoeffs[1] = coeffs[1]; +} + +void TwoZero :: setGain(MY_FLOAT aValue) +{ + gain = aValue; +} + +MY_FLOAT TwoZero :: tick(MY_FLOAT sample) // Perform Filter Operation +{ // TwoZero is a two zero filter (duh!) + // Look it up in your favorite DSP text + lastOutput = zeroCoeffs[0] * inputs[0]; + lastOutput += zeroCoeffs[1] * inputs[1]; + inputs[1] = inputs[0]; + inputs[0] = gain * sample; + lastOutput += inputs[0]; + return lastOutput; +} + diff --git a/TwoZero.h b/STK/TwoZero.h similarity index 100% rename from TwoZero.h rename to STK/TwoZero.h diff --git a/Vibraphn.cpp b/STK/Vibraphn.cpp similarity index 68% rename from Vibraphn.cpp rename to STK/Vibraphn.cpp index 0a15541..887c343 100644 --- a/Vibraphn.cpp +++ b/STK/Vibraphn.cpp @@ -13,22 +13,25 @@ Vibraphn :: Vibraphn() : Modal4() { - wave = new RawInterp("rawwaves/marmstk1.raw"); - wave->normalize(); - wave->setRate((MY_FLOAT) 13.33); - vibr->setFreq((MY_FLOAT) 4.0); - onepole->setPole((MY_FLOAT) 0.2); - this->setRatioAndReson(0, (MY_FLOAT) 1.0,(MY_FLOAT) 0.99995); /* Set */ - this->setRatioAndReson(1, (MY_FLOAT) 2.01,(MY_FLOAT) 0.99991); /* our */ - this->setRatioAndReson(2, (MY_FLOAT) 3.9,(MY_FLOAT) 0.99992); /* resonance */ - this->setRatioAndReson(3,(MY_FLOAT) 14.37,(MY_FLOAT) 0.99990); /* values here */ - this->setFiltGain(0,(MY_FLOAT) 0.025); - this->setFiltGain(1,(MY_FLOAT) 0.015); - this->setFiltGain(2,(MY_FLOAT) 0.015); - this->setFiltGain(3,(MY_FLOAT) 0.015); - masterGain = (MY_FLOAT) 1.0; - directGain = (MY_FLOAT) 0.0; - vibrGain = (MY_FLOAT) 0.2; + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + wave = new RawWvIn(strcat(file,"rawwaves/marmstk1.raw"),"oneshot"); + wave->normalize(); + wave->setRate((MY_FLOAT) 13.33); + vibr->setFreq((MY_FLOAT) 4.0); + onepole->setPole((MY_FLOAT) 0.2); + this->setRatioAndReson(0, (MY_FLOAT) 1.0,(MY_FLOAT) 0.99995); // Set + this->setRatioAndReson(1, (MY_FLOAT) 2.01,(MY_FLOAT) 0.99991); // our + this->setRatioAndReson(2, (MY_FLOAT) 3.9,(MY_FLOAT) 0.99992); // resonance + this->setRatioAndReson(3,(MY_FLOAT) 14.37,(MY_FLOAT) 0.99990); // values here + this->setFiltGain(0,(MY_FLOAT) 0.025); + this->setFiltGain(1,(MY_FLOAT) 0.015); + this->setFiltGain(2,(MY_FLOAT) 0.015); + this->setFiltGain(3,(MY_FLOAT) 0.015); + masterGain = (MY_FLOAT) 1.0; + directGain = (MY_FLOAT) 0.0; + vibrGain = (MY_FLOAT) 0.2; } Vibraphn :: ~Vibraphn() diff --git a/Vibraphn.h b/STK/Vibraphn.h similarity index 100% rename from Vibraphn.h rename to STK/Vibraphn.h diff --git a/VoicForm.cpp b/STK/VoicForm.cpp similarity index 78% rename from VoicForm.cpp rename to STK/VoicForm.cpp index 15d769b..b9ac6fb 100644 --- a/VoicForm.cpp +++ b/STK/VoicForm.cpp @@ -13,13 +13,7 @@ /* synthesis. In the floating point case */ /* cascade synthesis is the most natural */ /* so that's what you'll find here. */ -/* */ -/* For right now, there's a simple command*/ -/* line score interface consisting of 3 */ -/* letter symbols for the phonemes, =xx */ -/* sets the pitch to x, + and - add and */ -/* subtract a half step, and ... makes it */ -/* keep doing what it's doing for longer. */ +/* */ /*******************************************/ #include "VoicForm.h" @@ -27,7 +21,10 @@ VoicForm :: VoicForm() : Instrmnt() { - voiced = new SingWave("rawwaves/impuls40.raw"); + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); + voiced = new SingWave(strcat(file,"rawwaves/impuls20.raw")); voiced->normalize(); voiced->setGainRate((MY_FLOAT) 0.001); voiced->setGainTarget((MY_FLOAT) 0.0); @@ -81,6 +78,14 @@ void VoicForm :: clear() void VoicForm :: setFreq(MY_FLOAT frequency) { + MY_FLOAT temp; + if ((frequency * 22) > SRATE) { + printf("This note is too high!!\n"); + frequency = SRATE / 22; + } + lastFreq = frequency; + temp = fabs(1500 - frequency) + 200; + lastGain = 10000.0 / temp / temp; voiced->setFreq(frequency); } @@ -114,21 +119,17 @@ int VoicForm :: setPhoneme(char *phoneme) void VoicForm :: setVoiced(MY_FLOAT vGain) { voiced->setGainTarget(vGain); - //voiced->setGainTarget(vGain*0.5); } void VoicForm :: setUnVoiced(MY_FLOAT nGain) { noiseEnv->setTarget(nGain); - //noiseEnv->setTarget(nGain*0.5); } void VoicForm :: setVoicedUnVoiced(MY_FLOAT vGain, MY_FLOAT nGain) { this->setVoiced(vGain); - //this->setVoiced(vGain*0.5); this->setUnVoiced(nGain); - //this->setUnVoiced(nGain*0.5); } void VoicForm :: setFiltSweepRate(int whichOne,MY_FLOAT rate) @@ -154,16 +155,14 @@ void VoicForm :: quiet() void VoicForm :: noteOn(MY_FLOAT freq, MY_FLOAT amp) { + this->setFreq(freq); voiced->setGainTarget(amp); - //voiced->setGainTarget(amp*0.5); onepole->setPole((MY_FLOAT) 0.95 - (amp * (MY_FLOAT) NORM_7 * (MY_FLOAT) 0.2)); - //onepole->setPole((MY_FLOAT) 0.9 - (amp * (MY_FLOAT) 0.2)); - voiced->setFreq(freq); } void VoicForm :: noteOff(MY_FLOAT amp) { - voiced->noteOff(); + this->quiet(); } MY_FLOAT VoicForm :: tick() @@ -175,8 +174,7 @@ MY_FLOAT VoicForm :: tick() lastOutput = filters[1]->tick(lastOutput); lastOutput = filters[2]->tick(lastOutput); lastOutput = filters[3]->tick(lastOutput); - //lastOutput *= (MY_FLOAT) 0.02; - lastOutput *= (MY_FLOAT) 0.005; + lastOutput *= lastGain; return lastOutput; } @@ -193,32 +191,33 @@ void VoicForm :: controlChange(int number, MY_FLOAT value) this->setUnVoiced((MY_FLOAT) 0.01 * value * (MY_FLOAT) NORM_7); } else if (number == __SK_FootControl_) { - tempi = (int) (value / 2); - if (tempi < 16) { - tempi = tempi; - temp = (MY_FLOAT) 0.9; + tempi = (int) value; + if (tempi < 32) { + tempi = tempi; + temp = (MY_FLOAT) 0.9; } - else if (tempi < 32) { - tempi = tempi - 16; - temp = (MY_FLOAT) 1.0; + else if (tempi < 64) { + tempi = tempi - 32; + temp = (MY_FLOAT) 1.0; } - else if (tempi < 48) { - tempi = tempi - 32; - temp = (MY_FLOAT) 1.1; + else if (tempi < 96) { + tempi = tempi - 64; + temp = (MY_FLOAT) 1.1; } - else if (tempi <= 64) { - tempi = tempi - 48; - temp = (MY_FLOAT) 1.2; + else if (tempi <= 128) { + tempi = tempi - 96; + temp = (MY_FLOAT) 1.2; } this->setFormantAll(0,temp*(MY_FLOAT) phonParams[tempi][0][0], - (MY_FLOAT) phonParams[tempi][0][1], - (MY_FLOAT) pow(10.0,phonParams[tempi][0][2] / 20.0)); + (MY_FLOAT) phonParams[tempi][0][1], + (MY_FLOAT) pow(10.0,phonParams[tempi][0][2] / 20.0)); this->setFormantAll(1,temp*(MY_FLOAT) phonParams[tempi][1][0], - (MY_FLOAT) phonParams[tempi][1][1],(MY_FLOAT) 1.0); + (MY_FLOAT) phonParams[tempi][1][1],(MY_FLOAT) 1.0); this->setFormantAll(2,temp*(MY_FLOAT) phonParams[tempi][2][0], - (MY_FLOAT) phonParams[tempi][2][1],(MY_FLOAT) 1.0); + (MY_FLOAT) phonParams[tempi][2][1],(MY_FLOAT) 1.0); this->setFormantAll(3,temp*(MY_FLOAT) phonParams[tempi][3][0], - (MY_FLOAT) phonParams[tempi][3][1],(MY_FLOAT) 1.0); + (MY_FLOAT) phonParams[tempi][3][1],(MY_FLOAT) 1.0); + this->setVoicedUnVoiced(phonGains[tempi][0],phonGains[tempi][1]); } else if (number == __SK_ModFrequency_) voiced->setVibFreq(value * (MY_FLOAT) NORM_7 * (MY_FLOAT) 12.0); /* 0 to 12 Hz */ diff --git a/VoicForm.h b/STK/VoicForm.h similarity index 85% rename from VoicForm.h rename to STK/VoicForm.h index 43b3961..e43385c 100644 --- a/VoicForm.h +++ b/STK/VoicForm.h @@ -6,20 +6,14 @@ /* random and periodic vibrato, smoothing */ /* on frequency, etc.), excitation noise, */ /* and four sweepable complex resonances. */ -/* */ +/* */ /* Measured Formant data (from me) is */ /* included, and enough data is there to */ /* support either parallel or cascade */ /* synthesis. In the floating point case */ /* cascade synthesis is the most natural */ /* so that's what you'll find here. */ -/* */ -/* For right now, there's a simple command*/ -/* line score interface consisting of 3 */ -/* letter symbols for the phonemes, =xx */ -/* sets the pitch to x, + and - add and */ -/* subtract a half step, and ... makes it */ -/* keep doing what it's doing for longer. */ +/* */ /*******************************************/ #if !defined(__VoicForm_h) @@ -42,6 +36,8 @@ class VoicForm : public Instrmnt FormSwep *filters[4]; OnePole *onepole; OneZero *onezero; + MY_FLOAT lastFreq; + MY_FLOAT lastGain; public: VoicForm(); ~VoicForm(); diff --git a/VoicMang.cpp b/STK/VoicMang.cpp similarity index 100% rename from VoicMang.cpp rename to STK/VoicMang.cpp diff --git a/VoicMang.h b/STK/VoicMang.h similarity index 100% rename from VoicMang.h rename to STK/VoicMang.h diff --git a/STK/WavWvIn.cpp b/STK/WavWvIn.cpp new file mode 100644 index 0000000..4c6cb25 --- /dev/null +++ b/STK/WavWvIn.cpp @@ -0,0 +1,103 @@ +/*******************************************/ +/* WavWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to open DOS/Windows .wav 16-bit */ +/* data (signed integer) files for */ +/* playback. */ +/* */ +/* .wav files are always little-endian. */ +/*******************************************/ + +#include "WavWvIn.h" + +#ifndef __LITTLE_ENDIAN__ + #include "swapstuf.h" +#endif + +WavWvIn :: WavWvIn(char *fileName, char *mode) +{ + // Open the file and get header info + FILE *fd; + fd = fopen(fileName,"rb"); + if (!fd) { + printf("Couldn't open or find .wav file %s !!!\n",fileName); + exit(0); + } + + // Make sure this is a .wav format file + char wave[4]; + fseek(fd,8,SEEK_SET); // Locate wave id + fread(&wave,4,1,fd); + if (strncmp(wave,"WAVE",4)) { + printf("This doesn't appear to be a .wav file %s !!!\n",fileName); + exit(0); + } + + // Get number of channels from the header + INT16 temp; + fseek(fd,22,SEEK_SET); // Locate channels in header + fread(&temp,2,1,fd); +#ifndef __LITTLE_ENDIAN__ + temp = SwapINT16(temp); +#endif + channels = temp; + + // Get length of data from the header + INT32 bytes; + fseek(fd,40,SEEK_SET); // Locate data length in header + fread(&bytes,4,1,fd); +#ifndef __LITTLE_ENDIAN__ + bytes = SwapINT32(bytes); +#endif + length = bytes / 2 / channels; // length in 2-byte samples + data = (MY_FLOAT *) new MY_FLOAT[(length+1)*channels]; + + // Read samples into data[] + long i = 0; + while (fread(&temp,2,1,fd)) { +#ifndef __LITTLE_ENDIAN__ + temp = SwapINT16 (temp); +#endif + data[i++] = (MY_FLOAT) temp; + } + + // Get file sample rate from the header and set the default rate + INT32 srate; + fseek(fd,24,SEEK_SET); // Locate sample rate in header + fread(&srate,4,1,fd); +#ifndef __LITTLE_ENDIAN__ + srate = SwapINT32(srate); +#endif + rate = (MY_FLOAT) (srate/SRATE); // set default rate based on file sampling rate + fclose(fd); + + // Setup for looping or one-shot playback + if (!strcmp(mode,"looping")) { + looping = 1; + for (int j=0; j 0.0) interpolate = 1; + else interpolate = 0; + time = (MY_FLOAT) 0.0; + phaseOffset = (MY_FLOAT) 0.0; + finished = 0; + lastOutput = (MY_FLOAT *) calloc(channels, sizeof(MY_FLOAT)); +} + +WavWvIn :: ~WavWvIn() +{ +} diff --git a/STK/WavWvIn.h b/STK/WavWvIn.h new file mode 100644 index 0000000..0ad3078 --- /dev/null +++ b/STK/WavWvIn.h @@ -0,0 +1,26 @@ +/*******************************************/ +/* WavWvIn Input Class, */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This object inherits from WvIn and is */ +/* used to open DOS/Windows .wav 16-bit */ +/* data (signed integer) files for */ +/* playback. */ +/* */ +/* .wav files are always little-endian. */ +/*******************************************/ + +#if !defined(__WavWvIn_h) +#define __WavWvIn_h + +#include "Object.h" +#include "WvIn.h" + +class WavWvIn : public WvIn +{ + public: + WavWvIn(char *fileName, char *mode); + ~WavWvIn(); +}; + +#endif diff --git a/STK/WavWvOut.cpp b/STK/WavWvOut.cpp new file mode 100644 index 0000000..3cadb72 --- /dev/null +++ b/STK/WavWvOut.cpp @@ -0,0 +1,158 @@ +/*******************************************/ +/* Wave file Output Class, */ +/* by Perry R. Cook, 1995-96 */ +/* revised by Gary P. Scavone, 1999 */ +/* */ +/* This Object opens a DOS/Windows .wav */ +/* 16bit data (signed integers) file, and */ +/* poke buffers of samples into it. */ +/*******************************************/ + +#include "WavWvOut.h" +#ifndef __LITTLE_ENDIAN__ + #include "swapstuf.h" +#endif + +/******** Wav Soundfile Header Struct *******/ +struct wavehdr { + char riff[4]; // "RIFF" + INT32 file_size; // in bytes + char wave[4]; // "WAVE" + char fmt[4]; // "fmt " + INT32 block_size; // in bytes (generally 16) + INT16 format_tag; // 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM + INT16 num_chans; // 1=mono, 2=stereo + INT32 srate; + INT32 bytes_per_sec; + INT16 bytes_per_samp; // 2=16-bit mono, 4=16-bit stereo + INT16 bits_per_samp; + char data[4]; // "data" + INT32 dlength; // in bytes +}; + +FILE *openWAVFile(int chans,char *fileName) { + struct wavehdr hdr = {"RIF",44,"WAV","fmt", + 16,1,1,(INT32) SRATE,(INT32) SRATE*2,2,16,"dat",0}; + char tempName[128]; + FILE *fd; + + hdr.riff[3] = 'F'; + hdr.wave[3] = 'E'; + hdr.fmt[3] = ' '; + hdr.data[3] = 'a'; + + strcpy(tempName,fileName); + if (strstr(tempName,".wav") == NULL) strcat(tempName,".wav"); + hdr.num_chans = chans; + hdr.bytes_per_sec = (long) SRATE*2*chans; + hdr.bytes_per_samp = 2*chans; + hdr.bits_per_samp = 16; + fd = fopen(tempName,"wb"); + if (!fd) { + printf("Couldn't create soundfile %s !!!!!!!!\n",fileName); + exit(0); + } + +#ifndef __LITTLE_ENDIAN__ + hdr.file_size = SwapINT32(hdr.file_size); + hdr.block_size = SwapINT32(hdr.block_size); + hdr.format_tag = SwapINT16(hdr.format_tag); + hdr.num_chans = SwapINT16(hdr.num_chans); + hdr.srate = SwapINT32(hdr.srate); + hdr.bytes_per_sec = SwapINT32(hdr.bytes_per_sec); + hdr.bytes_per_samp = SwapINT16(hdr.bytes_per_samp); + hdr.bits_per_samp = SwapINT16(hdr.bits_per_samp); +#endif + + printf("\nCreating soundfile %s\n\n", tempName); + fwrite(&hdr,4,11,fd); + return fd; +} + +WavWvOut :: WavWvOut(char *fileName) +{ + channels = 1; + fd = openWAVFile(channels,fileName); + counter = 0; + totalCount = 0; +} + +WavWvOut :: WavWvOut(int chans, char *fileName) +{ + if (chans > 2) { + fprintf(stderr,"WavWvOut: Unsupported # of channels: %d\n", chans); + exit(0); + } + channels = chans; + fd = openWAVFile(chans,fileName); + counter = 0; + totalCount = 0; +} + +WavWvOut :: ~WavWvOut() +{ + MY_FLOAT time; + INT32 bytes; + + fwrite(data,2,counter,fd); + fseek(fd,40,SEEK_SET); + time = (double) totalCount * ONE_OVER_SRATE; + printf("%f Seconds Computed\n",time); + bytes = totalCount*2*channels; +#ifndef __LITTLE_ENDIAN__ + bytes = SwapINT32(bytes); +#endif + fwrite(&bytes,4,1,fd); + bytes = totalCount*2*channels + 44; +#ifndef __LITTLE_ENDIAN__ + bytes = SwapINT32(bytes); +#endif + fseek(fd,4,SEEK_SET); + fwrite(&bytes,4,1,fd); + fclose(fd); +} + +long WavWvOut :: getCounter() +{ + return totalCount; +} + +MY_FLOAT WavWvOut :: getTime() +{ + return (MY_FLOAT) totalCount * ONE_OVER_SRATE; +} + +void WavWvOut :: tick(MY_FLOAT sample) +{ + INT16 isample; + + isample = (INT16) (sample * 32000.0); +#ifndef __LITTLE_ENDIAN__ + isample = SwapINT16 (isample); +#endif + for (int i=0;iloadWaves(strcat(file1,"rawwaves/sinewave.raw"), + strcat(file2,"rawwaves/sinewave.raw"), + strcat(file3,"rawwaves/sinewave.raw"), + strcat(file4,"rawwaves/fwavblnk.raw")); + + this->setRatio(0,(MY_FLOAT) 1.0); + this->setRatio(1,(MY_FLOAT) 4.0); + this->setRatio(2,(MY_FLOAT) -510.0); + this->setRatio(3,(MY_FLOAT) -510.0); + gains[0] = __FM4Op_gains[99]; + gains[1] = __FM4Op_gains[82]; + gains[2] = __FM4Op_gains[92]; + gains[3] = __FM4Op_gains[68]; /* Originally 78, but sounded stinky */ + twozero->setGain((MY_FLOAT) 2.0); + adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.25,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + adsr[3]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.15,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); + vibWave->setFreq((MY_FLOAT) 8.0); +} + +void Wurley :: setFreq(MY_FLOAT frequency) +{ + baseFreq = frequency; + waves[0]->setFreq(baseFreq * ratios[0]); + waves[1]->setFreq(baseFreq * ratios[1]); + waves[2]->setFreq(ratios[2]); /* Note here a 'fixed resonance' */ + waves[3]->setFreq(ratios[3]); +} + +void Wurley :: noteOn(MY_FLOAT freq, MY_FLOAT amp) +{ + gains[0] = amp * __FM4Op_gains[99]; + gains[1] = amp * __FM4Op_gains[82]; + gains[2] = amp * __FM4Op_gains[82]; /* Originally 92 */ + gains[3] = amp * __FM4Op_gains[68]; /* Originally 78 */ + this->setFreq(freq); + this->keyOn(); +#if defined(_debug_) + printf("Wurley : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); +#endif +} diff --git a/Wurley.h b/STK/Wurley.h similarity index 100% rename from Wurley.h rename to STK/Wurley.h diff --git a/STK/WvIn.cpp b/STK/WvIn.cpp new file mode 100644 index 0000000..e5fd32d --- /dev/null +++ b/STK/WvIn.cpp @@ -0,0 +1,222 @@ +/********************************************/ +/* Data Input Base Class */ +/* by Gary P. Scavone, 1999 */ +/* */ +/* This class can handle multi-channel */ +/* input. Multi-channel input is */ +/* interleaved in the vector "data". */ +/* Actual data input occurs in the */ +/* subclasses of WvIn. */ +/********************************************/ + +#include "WvIn.h" +#include + +WvIn :: WvIn() +{ +} + +WvIn :: ~WvIn() +{ + if (data) { + delete [ ] data; + data = 0; + } + if (lastOutput) { + free(lastOutput); + lastOutput = 0; + } +} + +void WvIn :: reset() +{ + finished = 0; + time = (MY_FLOAT) 0.0; + for (int i=0;inormalize((MY_FLOAT) 1.0); +} + +// Normalize all channels equally by the greatest magnitude in all of data +void WvIn :: normalize(MY_FLOAT newPeak) +{ + long i; + MY_FLOAT max = (MY_FLOAT) 0.0; + + for (i=0;i max) + max = (MY_FLOAT) fabs((double) data[i]); + } + if (max > 0.0) { + max = (MY_FLOAT) 1.0 / max; + max *= newPeak; + for (i=0;i<=channels*length;i++) + data[i] *= max; + } +} + +void WvIn :: setRate(MY_FLOAT aRate) +{ + rate = aRate; + if (fmod(rate, 1.0) > 0.0) interpolate = 1; + else interpolate = 0; +} + +void WvIn :: setFreq(MY_FLOAT aFreq) +{ + rate = length * (MY_FLOAT) ONE_OVER_SRATE * aFreq; + if (fmod(rate, 1.0) > 0.0) interpolate = 1; + else interpolate = 0; +} + +void WvIn :: addTime(MY_FLOAT aTime) /* Add an absolute time */ +{ /* in samples */ + time += aTime; +} + +void WvIn :: addPhase(MY_FLOAT anAngle) /* Add a time in cycles */ +{ /* Cycles here means */ + time += length * anAngle; /* 1.0 = length */ +} + +void WvIn :: addPhaseOffset(MY_FLOAT anAngle) +{ /* Add a phase offset */ + phaseOffset = length * anAngle; /* in cycles, where */ +} /* 1.0 = length */ + +void WvIn :: setInterpolate(int anInterpStatus) +{ + interpolate = anInterpStatus; +} + +void WvIn :: setLooping(int aLoopStatus) +{ + time = (MY_FLOAT) 0.0; + looping = aLoopStatus; + + if (looping) { + for (int i=0;iinformTick(); + if (channels > 1) { + MY_FLOAT tempout = 0.0; + for (int i=0;iinformTick(); + return lastOutput; +} + +int WvIn :: informTick() +{ + static MY_FLOAT temp_time, alpha; + static long temp; + + if (!finished) { + + temp_time = time; + + if (phaseOffset != 0.0) { + temp_time += phaseOffset; /* Add phase offset */ + if (looping) { + while (temp_time >= length) /* Check for end of sound */ + temp_time -= length; /* loop back to beginning */ + while (temp_time < 0.0) /* Check for end of sound */ + temp_time += length; /* loop back to beginning */ + } + else { + if (temp_time >= length) /* Check for end of sound */ + temp_time = length - (MY_FLOAT) 1; /* stick at end */ + else if (temp_time < 0.0) /* check for end of sound */ + temp_time = (MY_FLOAT) 0.0; /* stick at beginning */ + } + } + + temp = (long) temp_time; /* Integer part of time address */ + + if (interpolate) { + alpha = temp_time - (MY_FLOAT) temp; /* fractional part of time address */ + temp *= channels; + for (int i=0;i= length) /* Check for end of sound */ + time -= length; /* loop back to beginning */ + while (time < 0.0) /* Check for end of sound */ + time += length; /* loop back to beginning */ + } + else { /* OneShot */ + if (time >= length) { /* Check for end of sound */ + time = length-(MY_FLOAT) 1; /* stick at end */ + finished = 1; /* Information for one-shot use */ + } + else if (time < 0.0) /* Check for end of sound */ + time = (MY_FLOAT) 0.0; /* stick at beginning */ + } + } + + return finished; +} + +MY_FLOAT WvIn :: lastOut() +{ + if (channels > 1) { + MY_FLOAT tempout = 0.0; + for (int i=0;i + +WvOut :: WvOut() +{ +} + +WvOut :: ~WvOut() +{ +} + +void WvOut :: tick(MY_FLOAT sample) +{ +} + +void WvOut :: mtick(MY_MULTI samples) +{ +} diff --git a/STK/WvOut.h b/STK/WvOut.h new file mode 100644 index 0000000..38a0858 --- /dev/null +++ b/STK/WvOut.h @@ -0,0 +1,27 @@ +/********************************************/ +/* WvOut Abstract Class, */ +/* by Tim Stilson, 1996 */ +/* revised by Gary P. Scavone, 1999 */ +/* */ +/* This class can handle multi-channel */ +/* data via the mtick() method. */ +/********************************************/ + +#include "Object.h" + +#if !defined(__WvOut_h) +#define __WvOut_h + + + +class WvOut : public Object +{ + public: + WvOut(); + virtual ~WvOut(); + virtual void tick(MY_FLOAT sample); + virtual void mtick(MY_MULTI samples); +}; + + +#endif // defined(__WvOut_h) diff --git a/mandplyr.cpp b/STK/mandplyr.cpp similarity index 100% rename from mandplyr.cpp rename to STK/mandplyr.cpp diff --git a/mandplyr.h b/STK/mandplyr.h similarity index 100% rename from mandplyr.h rename to STK/mandplyr.h diff --git a/phontabl.h b/STK/phontabl.h similarity index 100% rename from phontabl.h rename to STK/phontabl.h diff --git a/swapstuf.cpp b/STK/swapstuf.cpp similarity index 54% rename from swapstuf.cpp rename to STK/swapstuf.cpp index 5495132..96a40a2 100644 --- a/swapstuf.cpp +++ b/STK/swapstuf.cpp @@ -1,6 +1,8 @@ -int SwapInt (int inf) +#include "swapstuf.h" + +INT32 SwapINT32(INT32 inf) { - int o; + INT32 o; unsigned char *inp,*outp; inp=(unsigned char *)&inf; outp=(unsigned char *)&o; @@ -8,9 +10,9 @@ int SwapInt (int inf) return(o); } -short SwapShort (short inf) +INT16 SwapINT16(INT16 inf) { - short o; + INT16 o; unsigned char *inp,*outp; inp=(unsigned char *)&inf; outp=(unsigned char *)&o; @@ -18,7 +20,7 @@ short SwapShort (short inf) return(o); } -float SwapFloat (float inf) +float SwapFloat(float inf) { float o; unsigned char *inp,*outp; @@ -27,3 +29,14 @@ float SwapFloat (float inf) outp[0]=inp[3]; outp[1]=inp[2]; outp[2]=inp[1]; outp[3]=inp[0]; return(o); } + +double SwapDouble(double inf) +{ + double o; + unsigned char *inp,*outp; + inp=(unsigned char *)&inf; + outp=(unsigned char *)&o; + outp[0]=inp[7]; outp[1]=inp[6]; outp[2]=inp[5]; outp[3]=inp[4]; + outp[4]=inp[3]; outp[5]=inp[2]; outp[6]=inp[1]; outp[7]=inp[0]; + return(o); +} diff --git a/STK/swapstuf.h b/STK/swapstuf.h new file mode 100644 index 0000000..9ff9e5e --- /dev/null +++ b/STK/swapstuf.h @@ -0,0 +1,6 @@ +#include "Object.h" + +INT32 SwapINT32(INT32 inf); +INT16 SwapINT16(INT16 inf); +float SwapFloat(float inf); +double SwapDouble(float inf); diff --git a/STK98v2.ncb b/STK98v2.ncb deleted file mode 100644 index 08844988f414147cf946bf16efcb216e76bfb60e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1033216 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�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 z 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 -#endif - -int my_random(int max) { // Return Random Int Between 0 and max - unsigned long temp; - -#if defined(__OS_Linux_) /* RAND_MAX for Linux is 32 bit */ - temp = (unsigned long) rand() >> 16; -#else /* RAND_MAX for everything else is 16 bit */ - temp = (unsigned long) rand(); -#endif - temp *= (unsigned long) max; - temp >>= 15; - return (int) temp; -} - -MY_FLOAT noise_tick() // Return random MY_FLOAT float between -1.0 and 1.0 -{ - MY_FLOAT temp; - temp = (MY_FLOAT) (my_random(32767) - 16384); - temp *= 0.0000610351; - return temp; -} - -MY_FLOAT guiroScrape = 0; -MY_FLOAT shakeEnergy = 0.0; -MY_FLOAT input = 0.0,output[2] = {0.0,0.0}; -MY_FLOAT coeffs[2]; -MY_FLOAT input1 = 0.0,output1[2] = {0.0,0.0}; -MY_FLOAT coeffs1[2]; -MY_FLOAT input2 = 0.0,output2[2] = {0.0,0.0}; -MY_FLOAT coeffs2[2]; -MY_FLOAT input3 = 0.0,output3[2] = {0.0, 0.0}; -MY_FLOAT coeffs3[2]; -MY_FLOAT input4 = 0.0,output4[2] = {0.0, 0.0}; -MY_FLOAT coeffs4[2]; -MY_FLOAT sndLevel = 0.0; -MY_FLOAT gain = 0.0, gain1 = 0, gain2 = 0; -MY_FLOAT freq_rand = 0.0; -MY_FLOAT soundDecay = 0.0; -MY_FLOAT systemDecay = 0.0; -MY_FLOAT cymb_rand = 0.0; -long num_objects; -MY_FLOAT freq, freq1, freq2; - -MY_FLOAT collLikely,scrapeVel = 0.00015; -MY_FLOAT totalEnergy; -MY_FLOAT ratchet=0.0,ratchetDelta=0.0005; -MY_FLOAT finalZ[3] = {0.0, 0.0, 0.0}; - -/************************* MARACA *****************************/ -#define MARA_SOUND_DECAY 0.95 -#define MARA_SYSTEM_DECAY 0.999 -#define MARA_NUM_BEANS 25 - -void maraca_setup() { - num_objects = MARA_NUM_BEANS; - gain = log(num_objects) / log(4.0) * 40.0 / (MY_FLOAT) num_objects; - coeffs[0] = -0.96 * 2.0 * cos(3200.0 * TWO_PI / SRATE); - coeffs[1] = 0.96*0.96; - soundDecay = MARA_SOUND_DECAY; - systemDecay = MARA_SYSTEM_DECAY; -} - -MY_FLOAT maraca_tick() { - MY_FLOAT data; - shakeEnergy *= systemDecay; // Exponential system decay - if (my_random(1024) < num_objects) // If collision - sndLevel += gain * shakeEnergy; // add energy - input = sndLevel * noise_tick(); // Actual Sound is Random - sndLevel *= soundDecay; // Exponential Sound decay - input -= output[0]*coeffs[0]; // Do gourd - input -= output[1]*coeffs[1]; // resonance - output[1] = output[0]; // filter - output[0] = input; // calculations - data = output[0] - output[1]; // Extra zero for shape - return data; -} - -/*********************** SEKERE *****************************/ -#define SEKE_SOUND_DECAY 0.96 -#define SEKE_SYSTEM_DECAY 0.999 -#define SEKE_NUM_BEANS 64 - -void sekere_setup() { - num_objects = SEKE_NUM_BEANS; - gain = log(num_objects) / log(4.0) * 40.0 / (MY_FLOAT) num_objects; - coeffs[0] = -0.6 * 2.0 * cos(5500.0 * TWO_PI / SRATE); - coeffs[1] = 0.6*0.6; - soundDecay = SEKE_SOUND_DECAY; - systemDecay = SEKE_SYSTEM_DECAY; -} - -MY_FLOAT sekere_tick() { - MY_FLOAT data; - shakeEnergy *= systemDecay; // Exponential system decay - if (my_random(1024) < num_objects) // If collision - sndLevel += gain * shakeEnergy; // add energy - input = sndLevel * noise_tick(); // Actual Sound is Random - sndLevel *= soundDecay; // Exponential Sound decay - input -= output[0]*coeffs[0]; // Do gourd - input -= output[1]*coeffs[1]; // resonance - output[1] = output[0]; // filter - output[0] = input; // calculations - finalZ[2] = finalZ[1]; - finalZ[1] = finalZ[0]; - finalZ[0] = output[1]; - data = finalZ[0] - finalZ[2]; - return data * 2; // Normalization hack -} - -/************************ CABASA ***************************/ -#define CABA_SOUND_DECAY 0.95 -#define CABA_SYSTEM_DECAY 0.997 -#define CABA_NUM_BEADS 512 - -void cabasa_setup() { - num_objects = CABA_NUM_BEADS; - gain = log(num_objects) / log(4.0) * 120.0 / (MY_FLOAT) num_objects; - coeffs[0] = -0.7 * 2.0 * cos(3000.0 * TWO_PI / SRATE); - coeffs[1] = 0.7*0.7; - soundDecay = CABA_SOUND_DECAY; - systemDecay = CABA_SYSTEM_DECAY; -} - -MY_FLOAT cabasa_tick() { - MY_FLOAT data; - shakeEnergy *= systemDecay; // Exponential system decay - if (my_random(1024) < num_objects) // If collision - sndLevel += gain * shakeEnergy; // add energy - input = sndLevel * noise_tick(); // Actual Sound is Random - sndLevel *= soundDecay; // Exponential Sound decay - input -= output[0]*coeffs[0]; // Do gourd - input -= output[1]*coeffs[1]; // resonance - output[1] = output[0]; // filter - output[0] = input; // calculations - data = output[0] - output[1]; - return data * 2; // Normalization hack -} - -/************************ Bamboo Wind Chimes *****************/ -#define BAMB_SOUND_DECAY 0.95 -#define BAMB_SYSTEM_DECAY 0.99995 -#define BAMB_NUM_TUBES 5 -#define BAMB_BASE_FREQ 2800 - -void bamboo_setup() { - num_objects = BAMB_NUM_TUBES; - soundDecay = BAMB_SOUND_DECAY; - systemDecay = BAMB_SYSTEM_DECAY; - gain = 4.0 / (MY_FLOAT) num_objects; - coeffs[0] = -0.995 * 2.0 * cos(BAMB_BASE_FREQ * TWO_PI / SRATE); - coeffs[1] = 0.995*0.995; - coeffs1[0] = -0.995 * 2.0 * cos(BAMB_BASE_FREQ * 0.8 * TWO_PI / SRATE); - coeffs1[1] = 0.995*0.995; - coeffs2[0] = -0.995 * 2.0 * cos(BAMB_BASE_FREQ * 1.2 * TWO_PI / SRATE); - coeffs2[1] = 0.995*0.995; -} - -MY_FLOAT bamboo_tick() { - MY_FLOAT data; - shakeEnergy *= systemDecay; // Exponential System Decay - if (my_random(4096) < num_objects) { - sndLevel += gain * shakeEnergy; - freq_rand = BAMB_BASE_FREQ * (1.0 + (0.2 * noise_tick())); - coeffs[0] = -0.995 * 2.0 * - cos(freq_rand * TWO_PI / SRATE); - freq_rand = BAMB_BASE_FREQ * (0.8 + (0.2 * noise_tick())); - coeffs1[0] = -0.995 * 2.0 * - cos(freq_rand * TWO_PI / SRATE); - freq_rand = BAMB_BASE_FREQ * (1.2 + (0.2 * noise_tick())); - coeffs2[0] = -0.995 * 2.0 * - cos(freq_rand * TWO_PI / SRATE); - } - input = sndLevel; - input *= noise_tick(); // Actual Sound is Random - input1 = input; - input2 = input; - - sndLevel *= soundDecay; // Each (all) event(s) - // decay(s) exponentially - input -= output[0]*coeffs[0]; - input -= output[1]*coeffs[1]; - output[1] = output[0]; - output[0] = input; - data = output[0]; - input1 -= output1[0]*coeffs1[0]; - input1 -= output1[1]*coeffs1[1]; - output1[1] = output1[0]; - output1[0] = input1; - data += output1[0]; - input2 -= output2[0]*coeffs2[0]; - input2 -= output2[1]*coeffs2[1]; - output2[1] = output2[0]; - output2[0] = input2; - data += output2[0]; - return data * 0.5; // Normalization hack -} - -/******************* Water Drops ******************************/ -#define WUTR_SOUND_DECAY 0.95 -#define WUTR_NUM_SOURCES 4 -#define WUTR_FILT_POLE 0.9985 -#define WUTR_FREQ_SWEEP 1.0001 -#define WUTR_BASE_FREQ 600 - -void wuter_setup() { - num_objects = WUTR_NUM_SOURCES; - soundDecay = WUTR_SOUND_DECAY; - freq = WUTR_BASE_FREQ * TWO_PI / SRATE; - freq1 = WUTR_BASE_FREQ * 2.0 * TWO_PI / SRATE; - freq2 = WUTR_BASE_FREQ * 3.0 * TWO_PI / SRATE; - coeffs[0] = -WUTR_FILT_POLE * 2.0 * cos(freq); - coeffs[1] = WUTR_FILT_POLE * WUTR_FILT_POLE; - coeffs1[0] = -WUTR_FILT_POLE * 2.0 * cos(freq1); - coeffs1[1] = WUTR_FILT_POLE * WUTR_FILT_POLE; - coeffs2[0] = -WUTR_FILT_POLE * 2.0 * cos(freq2); - coeffs2[1] = WUTR_FILT_POLE * WUTR_FILT_POLE; -} -MY_FLOAT wuter_tick() { - MY_FLOAT data; - int j; - if (my_random(32767) < num_objects) { - sndLevel = shakeEnergy; - j = my_random(3); - if (j == 0) { - freq = WUTR_BASE_FREQ * (0.75 + (0.25 * noise_tick())); - gain = fabs(noise_tick()); - } - else if (j == 1) { - freq1 = WUTR_BASE_FREQ * (1.0 + (0.25 * noise_tick())); - gain1 = fabs(noise_tick()); - } - else { - freq2 = WUTR_BASE_FREQ * (1.25 + (0.25 * noise_tick())); - gain2 = fabs(noise_tick()); - } - } - - gain *= WUTR_FILT_POLE; - if (gain > 0.001) { - freq *= WUTR_FREQ_SWEEP; - coeffs[0] = -WUTR_FILT_POLE * 2.0 * - cos(freq * TWO_PI / SRATE); - } - gain1 *= WUTR_FILT_POLE; - if (gain1 > 0.001) { - freq1 *= WUTR_FREQ_SWEEP; - coeffs1[0] = -WUTR_FILT_POLE * 2.0 * - cos(freq1 * TWO_PI / SRATE); - } - gain2 *= WUTR_FILT_POLE; - if (gain2 > 0.001) { - freq2 *= WUTR_FREQ_SWEEP; - coeffs2[0] = -WUTR_FILT_POLE * 2.0 * - cos(freq2 * TWO_PI / SRATE); - } - - sndLevel *= soundDecay; // Each (all) event(s) - // decay(s) exponentially - input = sndLevel; - input *= noise_tick(); // Actual Sound is Random - input1 = input * gain1; - input2 = input * gain2; - input *= gain; - input -= output[0]*coeffs[0]; - input -= output[1]*coeffs[1]; - output[1] = output[0]; - output[0] = input; - data = output[0]; - input1 -= output1[0]*coeffs1[0]; - input1 -= output1[1]*coeffs1[1]; - output1[1] = output1[0]; - output1[0] = input1; - data += output1[0]; - input2 -= output2[0]*coeffs2[0]; - input2 -= output2[1]*coeffs2[1]; - output2[1] = output2[0]; - output2[0] = input2; - data += output2[0]; - - finalZ[2] = finalZ[1]; - finalZ[1] = finalZ[0]; - finalZ[0] = data * 4; - - data = finalZ[2] - finalZ[0]; - return data; -} - -/****************** TAMBOURINE ******************************/ -#define TAMB_SOUND_DECAY 0.95 -#define TAMB_SYSTEM_DECAY 0.9985 -#define TAMB_NUM_TIMBRELS 32 -#define TAMB_SHELL_FREQ 2300 -#define TAMB_SHELL_GAIN 0.1 -#define TAMB_CYMB_FREQ 5600 -#define TAMB_CYMB_FREQ2 8100 -#define TAMB_CYMB_RESON 0.99 - -void tambourine_setup() { - num_objects = TAMB_NUM_TIMBRELS; - soundDecay = TAMB_SOUND_DECAY; - systemDecay = TAMB_SYSTEM_DECAY; - gain = 24.0 / num_objects; - coeffs[0] = -0.96 * 2.0 * cos(TAMB_SHELL_FREQ * TWO_PI / SRATE); - coeffs[1] = 0.96*0.96; - coeffs1[0] = -TAMB_CYMB_RESON * 2.0 * cos(TAMB_CYMB_FREQ * TWO_PI / SRATE); - coeffs1[1] = TAMB_CYMB_RESON * TAMB_CYMB_RESON; - coeffs2[0] = -TAMB_CYMB_RESON * 2.0 * cos(TAMB_CYMB_FREQ2 * TWO_PI / SRATE); - coeffs2[1] = TAMB_CYMB_RESON * TAMB_CYMB_RESON; -} - -MY_FLOAT tambourine_tick() { - MY_FLOAT data; - shakeEnergy *= systemDecay; // Exponential System Decay - if (my_random(1024) < num_objects) { - sndLevel += gain * shakeEnergy; - cymb_rand = noise_tick() * TAMB_CYMB_FREQ * 0.05; - coeffs1[0] = -TAMB_CYMB_RESON * 2.0 * - cos((TAMB_CYMB_FREQ + cymb_rand) * TWO_PI / SRATE); - cymb_rand = noise_tick() * TAMB_CYMB_FREQ * 0.05; - coeffs2[0] = -TAMB_CYMB_RESON * 2.0 * - cos((TAMB_CYMB_FREQ2 + cymb_rand) * TWO_PI / SRATE); - } - input = sndLevel; - input *= noise_tick(); // Actual Sound is Random - input1 = input * 0.8; - input2 = input; - input *= TAMB_SHELL_GAIN; - - sndLevel *= soundDecay; // Each (all) event(s) - // decay(s) exponentially - input -= output[0]*coeffs[0]; - input -= output[1]*coeffs[1]; - output[1] = output[0]; - output[0] = input; - data = output[0]; - input1 -= output1[0]*coeffs1[0]; - input1 -= output1[1]*coeffs1[1]; - output1[1] = output1[0]; - output1[0] = input1; - data += output1[0]; - input2 -= output2[0]*coeffs2[0]; - input2 -= output2[1]*coeffs2[1]; - output2[1] = output2[0]; - output2[0] = input2; - data += output2[0]; - - finalZ[2] = finalZ[1]; - finalZ[1] = finalZ[0]; - finalZ[0] = data; - data = finalZ[2] - finalZ[0]; - - return data; -} - -/************************ SLEIGHBELLS **************************/ -#define SLEI_SOUND_DECAY 0.97 -#define SLEI_SYSTEM_DECAY 0.9994 -#define SLEI_NUM_BELLS 32 -#define SLEI_CYMB_FREQ0 2500 -#define SLEI_CYMB_FREQ1 5300 -#define SLEI_CYMB_FREQ2 6500 -#define SLEI_CYMB_FREQ3 8300 -#define SLEI_CYMB_FREQ4 9800 -#define SLEI_CYMB_RESON 0.99 - -void sleighbell_setup() { - num_objects = SLEI_NUM_BELLS; - soundDecay = SLEI_SOUND_DECAY; - systemDecay = SLEI_SYSTEM_DECAY; - gain = 8.0 / num_objects; - coeffs[0] = -SLEI_CYMB_RESON * 2.0 * cos(SLEI_CYMB_FREQ0 * TWO_PI / SRATE); - coeffs[1] = SLEI_CYMB_RESON*SLEI_CYMB_RESON; - coeffs1[0] = -SLEI_CYMB_RESON * 2.0 * cos(SLEI_CYMB_FREQ1 * TWO_PI / SRATE); - coeffs1[1] = SLEI_CYMB_RESON*SLEI_CYMB_RESON; - coeffs2[0] = -SLEI_CYMB_RESON * 2.0 * cos(SLEI_CYMB_FREQ2 * TWO_PI / SRATE); - coeffs2[1] = SLEI_CYMB_RESON*SLEI_CYMB_RESON; - coeffs3[0] = -SLEI_CYMB_RESON * 2.0 * cos(SLEI_CYMB_FREQ3 * TWO_PI / SRATE); - coeffs3[1] = SLEI_CYMB_RESON*SLEI_CYMB_RESON; - coeffs4[0] = -SLEI_CYMB_RESON * 2.0 * cos(SLEI_CYMB_FREQ4 * TWO_PI / SRATE); - coeffs4[1] = SLEI_CYMB_RESON*SLEI_CYMB_RESON; -} - -MY_FLOAT sleighbell_tick() { - MY_FLOAT data; - shakeEnergy *= systemDecay; // Exponential System Decay - if (my_random(1024) < num_objects) { - sndLevel += gain * shakeEnergy; - cymb_rand = noise_tick() * SLEI_CYMB_FREQ0 * 0.05; - coeffs[0] = -SLEI_CYMB_RESON * 2.0 * - cos((SLEI_CYMB_FREQ0 + cymb_rand) * TWO_PI / SRATE); - cymb_rand = noise_tick() * SLEI_CYMB_FREQ1 * 0.03; - coeffs1[0] = -SLEI_CYMB_RESON * 2.0 * - cos((SLEI_CYMB_FREQ1 + cymb_rand) * TWO_PI / SRATE); - cymb_rand = noise_tick() * SLEI_CYMB_FREQ2 * 0.03; - coeffs2[0] = -SLEI_CYMB_RESON * 2.0 * - cos((SLEI_CYMB_FREQ2 + cymb_rand) * TWO_PI / SRATE); - cymb_rand = noise_tick() * SLEI_CYMB_FREQ3 * 0.03; - coeffs3[0] = -SLEI_CYMB_RESON * 2.0 * - cos((SLEI_CYMB_FREQ3 + cymb_rand) * TWO_PI / SRATE); - cymb_rand = noise_tick() * SLEI_CYMB_FREQ4 * 0.03; - coeffs4[0] = -SLEI_CYMB_RESON * 2.0 * - cos((SLEI_CYMB_FREQ4 + cymb_rand) * TWO_PI / SRATE); - } - input = sndLevel; - input *= noise_tick(); // Actual Sound is Random - input1 = input; - input2 = input; - input3 = input * 0.5; - input4 = input * 0.3; - - sndLevel *= soundDecay; // Each (all) event(s) - // decay(s) exponentially - input -= output[0]*coeffs[0]; - input -= output[1]*coeffs[1]; - output[1] = output[0]; - output[0] = input; - data = output[0]; - - input1 -= output1[0]*coeffs1[0]; - input1 -= output1[1]*coeffs1[1]; - output1[1] = output1[0]; - output1[0] = input1; - data += output1[0]; - - input2 -= output2[0]*coeffs2[0]; - input2 -= output2[1]*coeffs2[1]; - output2[1] = output2[0]; - output2[0] = input2; - data += output2[0]; - - input3 -= output3[0]*coeffs3[0]; - input3 -= output3[1]*coeffs3[1]; - output3[1] = output3[0]; - output3[0] = input3; - data += output3[0]; - - input4 -= output4[0]*coeffs4[0]; - input4 -= output4[1]*coeffs4[1]; - output4[1] = output4[0]; - output4[0] = input4; - data += output4[0]; - - finalZ[2] = finalZ[1]; - finalZ[1] = finalZ[0]; - finalZ[0] = data; - data = finalZ[2] - finalZ[0]; - - return data; -} - -/**************************** GUIRO ***********************/ -#define GUIR_SOUND_DECAY 0.95 -#define GUIR_NUM_RATCHETS 128 -#define GUIR_GOURD_FREQ 2500.0 -#define GUIR_GOURD_RESON 0.97 -#define GUIR_GOURD_FREQ2 4000.0 -#define GUIR_GOURD_RESON2 0.97 - -void guiro_setup() { - num_objects = GUIR_NUM_RATCHETS; - soundDecay = GUIR_SOUND_DECAY; - coeffs[0] = -GUIR_GOURD_RESON * 2.0 * cos(GUIR_GOURD_FREQ * TWO_PI / SRATE); - coeffs[1] = GUIR_GOURD_RESON*GUIR_GOURD_RESON; - coeffs2[0] = -GUIR_GOURD_RESON2 * 2.0 * cos(GUIR_GOURD_FREQ2 * TWO_PI / SRATE); - coeffs2[1] = GUIR_GOURD_RESON2*GUIR_GOURD_RESON2; - ratchet = 0; - guiroScrape = 0; -} - -MY_FLOAT guiro_tick() { - MY_FLOAT data; - if (my_random(1024) < num_objects) { - sndLevel += 512 * ratchet * totalEnergy; - } - input = sndLevel; - input *= noise_tick() * ratchet; - sndLevel *= soundDecay; - - input2 = input; - input -= output[0]*coeffs[0]; - input -= output[1]*coeffs[1]; - output[1] = output[0]; - output[0] = input; - input2 -= output2[0]*coeffs2[0]; - input2 -= output2[1]*coeffs2[1]; - output2[1] = output2[0]; - output2[0] = input2; - - finalZ[2] = finalZ[1]; - finalZ[1] = finalZ[0]; - finalZ[0] = output[1] + output2[1]; - data = finalZ[0] - finalZ[2]; - return data; -} - -/******************* THE ACTUAL CLASS ITSELF ***********************/ - -#include "Shakers.h" -#include "SKINI11.msg" - -Shakers :: Shakers() : Instrmnt() -{ - instType = 0; -} - -Shakers :: ~Shakers() -{ -} - -#define MAX_SHAKE 2000.0 - -void Shakers :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - shakeEnergy = amp * MAX_SHAKE * 0.1; -#if defined(_debug_) - printf("Shakers : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} - -void Shakers :: noteOff(MY_FLOAT amp) -{ - shakeEnergy = 0.0; -} - -MY_FLOAT Shakers :: tick() -{ - if (instType==0) { - lastOutput = maraca_tick(); - } - else if (instType==1) { - lastOutput = sekere_tick(); - } - else if (instType==2) { - lastOutput = cabasa_tick(); - } - else if (instType==3) { - lastOutput = bamboo_tick(); - } - else if (instType==4) { - lastOutput = wuter_tick(); - } - else if (instType==5) { - lastOutput = tambourine_tick(); - } - else if (instType==6) { - lastOutput = sleighbell_tick(); - } - else if (instType==7) { - if (guiroScrape < 1.0) { - guiroScrape += scrapeVel; - totalEnergy = guiroScrape; - ratchet -= (ratchetDelta + (0.002*totalEnergy)); - if (ratchet<0.0) ratchet = 1.0; - lastOutput = guiro_tick(); - } - else lastOutput = 0.0; - } - - lastOutput *= 0.0001; - - return lastOutput; -} - -void Shakers :: controlChange(int number, MY_FLOAT value) -{ - MY_FLOAT temp; -#if defined(_debug_) - printf("Shakers : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_Breath_) { -#if defined(_debug_) - printf("shaking \n"); -#endif - shakeEnergy += value * NORM_7 * MAX_SHAKE * 0.1; - if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE; - } - else if (number == __SK_FootControl_) { -#if defined(_debug_) - printf("setting decay\n"); -#endif - systemDecay = 0.998 + (value * NORM_7 * 0.002); - } - else if (number == __SK_ModFrequency_) { -#if defined(_debug_) - printf("setting number of beans\n"); -#endif - num_objects = (int) value; - gain = log(num_objects) * 30.0 / (MY_FLOAT) num_objects; - } - else if (number == __SK_ModWheel_) { -#if defined(_debug_) - printf("setting resonance freq.\n"); -#endif - temp = 900 * pow (1.015,value); - coeffs[0] = -0.96 * 2.0 * cos(temp * TWO_PI / SRATE); - coeffs[1] = 0.96*0.96; - } - else if (number == __SK_AfterTouch_Cont_) { -#if defined(_debug_) - printf("shaking \n"); -#endif - shakeEnergy += value * NORM_7 * MAX_SHAKE * 0.1; - if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE; - } - else if (number == __SK_ShakerInst_) { - instType = (int) (value + 0.5); // Just to be safe - if (instType==0) maraca_setup(); - else if (instType==1) sekere_setup(); - else if (instType==2) cabasa_setup(); - else if (instType==3) bamboo_setup(); - else if (instType==4) wuter_setup(); - else if (instType==5) tambourine_setup(); - else if (instType==6) sleighbell_setup(); - else if (instType==7) guiro_setup(); - else { - instType = 0; - maraca_setup(); - } - } - else { - printf("Shakers : Undefined Control Number!!\n"); - } -} - diff --git a/Shakers.h b/Shakers.h deleted file mode 100644 index 8a82883..0000000 --- a/Shakers.h +++ /dev/null @@ -1,35 +0,0 @@ -/**********************************************************/ -/* PhISEM (Physically Informed Stochastic Event Modeling */ -/* by Perry R. Cook, Princeton, February 1997 */ -/* Meta-model that simulates all of: */ -/* Maraca Simulation by Perry R. Cook, Princeton, 1996-7 */ -/* Sekere Simulation by Perry R. Cook, Princeton, 1996-7 */ -/* Cabasa Simulation by Perry R. Cook, Princeton, 1996-7 */ -/* Bamboo Windchime Simulation, by Perry R. Cook, 1996-7 */ -/* Water Drops Simulation, by Perry R. Cook, 1996-7 */ -/* Tambourine Simulation, by Perry R. Cook, 1996-7 */ -/* Sleighbells Simulation, by Perry R. Cook, 1996-7 */ -/* Guiro Simulation, by Perry R. Cook, 1996-7 */ -/**********************************************************/ - - -#if !defined(__Shakers_h) -#define __Shakers_h - -#include "Instrmnt.h" - - -class Shakers : public Instrmnt -{ - protected: - int instType; - public: - Shakers(); - ~Shakers(); - virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); - virtual void noteOff(MY_FLOAT amp); - MY_FLOAT tick(); - virtual void controlChange(int number, MY_FLOAT value); -}; - -#endif diff --git a/Simple.cpp b/Simple.cpp deleted file mode 100644 index c05b738..0000000 --- a/Simple.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/*******************************************/ -/* Master Class for Simple Instrument */ -/* by Perry R. Cook, 1995-96 */ -/* This instrument contains 1 looped */ -/* wave, 1 noise source, 1 biquad filter */ -/* 1 one-pole filter, and 1 ADSR envelope */ -/*******************************************/ - -#include "Simple.h" - -Simple :: Simple() -{ - MY_FLOAT coeffs[2]; - adsr = new ADSR; - baseFreq = (MY_FLOAT) 440.0; - loop = new RawLoop("rawwaves/impuls10.raw"); - loop->normalize(); - filter = new OnePole; - noise = new Noise; - bqpoles = new TwoPole; - bqzeroes = new TwoZero; - coeffs[0] = 0; - coeffs[1] = -1; - bqzeroes->setZeroCoeffs(coeffs); - filter->setPole(0.5); - this->setFreq(baseFreq); - loopGain = 0.5; -} - -Simple :: ~Simple() -{ - delete adsr; - delete loop; - delete filter; - delete bqzeroes; - delete bqpoles; -} - -void Simple :: keyOn() -{ - adsr->keyOn(); -} - -void Simple :: keyOff() -{ - adsr->keyOff(); -} - -void Simple :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - this->keyOn(); - this->setFreq(freq); - filter->setGain(amp); -#if defined(_debug_) - printf("Simple : NoteOn: Freq= %lf, Amp=%lf\n",freq, amp); -#endif -} -void Simple :: noteOff(MY_FLOAT amplitude) -{ - this->keyOff(); -#if defined(_debug_) - printf("Simple : NoteOff: Amp=%lf\n",amplitude); -#endif -} - -void Simple :: setFreq(MY_FLOAT frequency) -{ -#define R 0.98 - - MY_FLOAT coeffs[2]; - coeffs[0] = 2 * R * cos(TWO_PI * ONE_OVER_SRATE * frequency); - coeffs[1] = - R * R; - bqpoles->setPoleCoeffs(coeffs); - bqpoles->setGain(1.0 - R); - loop->setFreq(frequency); -} - -MY_FLOAT Simple :: tick() -{ - lastOutput = loopGain * loop->tick(); - bqzeroes->tick(bqpoles->tick(noise->tick())); - lastOutput += (1.0 - loopGain) * bqzeroes->lastOut(); - lastOutput = filter->tick(lastOutput); - lastOutput *= adsr->tick(); - return lastOutput; -} - -#include "SKINI11.msg" - -void Simple :: controlChange(int number, MY_FLOAT value) -{ -#if defined(_debug_) - printf("Simple : ControlChange: Number=%i Value=%f\n",number,value); -#endif - if (number == __SK_Breath_) - filter->setPole(0.99 * (1.0 - (value * NORM_7 * 2.0))); - else if (number == __SK_NoiseLevel_) - loopGain = 1.0 - (NORM_7 * value); - else if (number == __SK_ModFrequency_) { - adsr->setAttackRate(value * NORM_7); - adsr->setDecayRate(value * NORM_7); - adsr->setReleaseRate(value * NORM_7); - } - else if (number == __SK_ModWheel_) - printf("Mod Wheel Unimplemented\n"); - else if (number == __SK_AfterTouch_Cont_) - adsr->setTarget(value * NORM_7); - else { - printf("Simple : Undefined Control Number!!\n"); - } -} diff --git a/Simple.h b/Simple.h deleted file mode 100644 index 3084cbe..0000000 --- a/Simple.h +++ /dev/null @@ -1,44 +0,0 @@ -/*******************************************/ -/* Master Class for Simple Instrument */ -/* by Perry R. Cook, 1995-96 */ -/* This instrument contains 1 looped */ -/* wave, 1 noise source, 1 biquad filter */ -/* 1 one-pole filter, and 1 ADSR envelope */ -/*******************************************/ - -#if !defined(__Simple_h) -#define __Simple_h - -#include "Instrmnt.h" -#include "ADSR.h" -#include "RawLoop.h" -#include "OnePole.h" -#include "TwoPole.h" -#include "TwoZero.h" -#include "Noise.h" - -class Simple : public Instrmnt -{ - protected: - ADSR *adsr; - RawLoop *loop; - OnePole *filter; - TwoPole *bqpoles; - TwoZero *bqzeroes; - Noise *noise; - MY_FLOAT baseFreq; - MY_FLOAT loopGain; - public: - Simple(); - ~Simple(); - void clear(); - virtual void setFreq(MY_FLOAT frequency); - void keyOn(); - void keyOff(); - virtual void noteOn(MY_FLOAT freq, MY_FLOAT amp); - virtual void noteOff(MY_FLOAT amplitude); - virtual MY_FLOAT tick(); - virtual void controlChange(int number, MY_FLOAT value); -}; - -#endif diff --git a/SndWvOut.cpp b/SndWvOut.cpp deleted file mode 100644 index 0dca7ee..0000000 --- a/SndWvOut.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/*******************************************/ -/* NeXT Sound File Output Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This version opens a mono NeXT .snd */ -/* file 16bit data at 22KHz, and */ -/* pokes buffers of samples into it. */ -/*******************************************/ - -/*******************************************/ -/* NeXT Soundfile Output Class, Improved */ -/* by Perry R. Cook, 1996 */ -/* This one opens a NeXT .snd file, and */ -/* even knows how to byte-swap! */ -/*******************************************/ - -#include "SndWvOut.h" - -#ifdef __LITTLE_ENDIAN__ - #include "swapstuf.h" -#endif - -/******** NeXT Soundfile Header Struct *******/ -struct headerform { - char pref[4]; - long hdr_length; - long file_length; - long mode; - long samp_rate; - long num_channels; - char comment[16]; -}; - -FILE *openNeXTFile(int chans,char *fileName) { - struct headerform hdr = {".sn",40,0,3,(long) SRATE,1,"STK98"}; - char tempName[128]; - FILE *fd; - - hdr.pref[3] = 'd'; - - strcpy(tempName,fileName); - strcat(tempName,".snd"); - hdr.num_channels = chans; - fd = fopen(tempName,"wb"); - if (!fd) { - printf("Couldn't create soundfile %s !!!!!!!!\n",fileName); - exit(0); - } -#ifdef __LITTLE_ENDIAN__ - hdr.hdr_length = SwapInt (hdr.hdr_length); - hdr.file_length = SwapInt (hdr.file_length); - hdr.mode = SwapInt (hdr.mode); - hdr.samp_rate = SwapInt (hdr.samp_rate); - hdr.num_channels = SwapInt (hdr.num_channels); -#endif - printf("Creating soundfile %s.\n", tempName); - fwrite(&hdr,4,10,fd); - return fd; -} - -SndWvOut :: SndWvOut(char *fileName) -{ - chans = 1; - pan = 0.5; - fd = openNeXTFile(chans,fileName); - counter = 0; - totalCount = 0; -} - -SndWvOut :: SndWvOut(int channels, char *fileName) -{ - chans = channels; - pan = 0.5; - fd = openNeXTFile(chans,fileName); - counter = 0; - totalCount = 0; -} - -SndWvOut :: ~SndWvOut() -{ - double temp; - - fwrite(data,2,counter,fd); - fseek(fd,8,SEEK_SET); - temp = (double) totalCount * ONE_OVER_SRATE; - printf("%f Seconds Computed\n",temp); - totalCount *= 2*chans; -#ifdef __LITTLE_ENDIAN__ - totalCount = SwapInt (totalCount); -#endif - fwrite(&totalCount,4,1,fd); - fclose(fd); -} - -long SndWvOut :: getCounter() -{ - return totalCount; -} - -MY_FLOAT SndWvOut :: getTime() -{ - return (MY_FLOAT) totalCount * ONE_OVER_SRATE; -} - -void SndWvOut :: tick(MY_FLOAT sample) -{ - short isample; - - isample = (short) (sample * 32000.0); - if (chans==1) { - data[counter] = isample; -#ifdef __LITTLE_ENDIAN__ - data[counter] = SwapShort (data[counter]); -#endif - counter += 1; - } - else { - data[counter] = (short) (isample * (1.0 - pan)); - data[counter+1] = (short) (isample * pan); -#ifdef __LITTLE_ENDIAN__ - data[counter] = SwapShort (data[counter]); - data[counter+1] = SwapShort (data[counter+1]); -#endif - counter += 2; - } - totalCount += 1; - if (counter == SND_BUFFER_SIZE) { - fwrite(data,2,SND_BUFFER_SIZE,fd); - counter = 0; - } -} - -void SndWvOut :: tick(MY_FLOAT lsamp, MY_FLOAT rsamp) -{ - if (chans==1) { - data[counter] = (short) ((lsamp + rsamp) * 16000.0); -#ifdef __LITTLE_ENDIAN__ - data[counter] = SwapShort (data[counter]); -#endif - counter += 1; - } - else { - data[counter] = (short) (lsamp * 32000.0); - data[counter+1] = (short) (rsamp * 32000.0); -#ifdef __LITTLE_ENDIAN__ - data[counter] = SwapShort (data[counter]); - data[counter+1] = SwapShort (data[counter+1]); -#endif - counter += 2; - } - totalCount += 1; - if (counter == SND_BUFFER_SIZE) { - fwrite(data,2,SND_BUFFER_SIZE,fd); - counter = 0; - } -} - -void SndWvOut :: setMonoPan(MY_FLOAT aPan) -{ - pan = aPan; - if (aPan < 0.0) { - pan = 0.0; - printf("Pan < 0.0, correcting to 0.0\n"); - } - if (aPan > 1.0) { - pan = 1.0; - printf("Pan > 1.0, correcting to 1.0\n"); - } -} diff --git a/SndWvOut.h b/SndWvOut.h deleted file mode 100644 index 4c7f9b7..0000000 --- a/SndWvOut.h +++ /dev/null @@ -1,44 +0,0 @@ -/*******************************************/ -/* NeXT Sound File Output Class, */ -/* by Perry R. Cook, 1995-96 */ -/* For now, This Object can open a raw */ -/* 16bit data (signed integers) file, and */ -/* poke buffers of samples into it. */ -/*******************************************/ - -/*******************************************/ -/* NeXT Soundfile Output Class, Improved */ -/* by Perry R. Cook, 1996 */ -/* This one opens a NeXT .snd file, and */ -/* even knows how to byte-swap! */ -/*******************************************/ - -#include "Object.h" -#include "WvOut.h" - -#if !defined(__SndWvOut_h) -#define __SndWvOut_h - -#define SND_BUFFER_SIZE 1024 - -class SndWvOut : public WvOut -{ - protected: - FILE *fd; - short data[SND_BUFFER_SIZE]; - long counter; - long totalCount; - int chans; - MY_FLOAT pan; - public: - SndWvOut(char *fileName); - SndWvOut(int channels, char *fileName); - ~SndWvOut(); - long getCounter(); - MY_FLOAT getTime(); - void setMonoPan(MY_FLOAT aPan); - void tick(MY_FLOAT sample); - void tick(MY_FLOAT lsamp, MY_FLOAT rsamp); -}; - -#endif // defined(__SndWvOut_h) diff --git a/TCLSpecs/.swp b/TCLSpecs/.swp deleted file mode 100644 index 1bd546be970924b3eaa395e8bd1fdd386b8d45a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 zcmeI(Jq`gu6ae7I5jcQFDUoHx9TcLXUnD9bqH$aY5q61o8!Or3o8-+;X7b+Tt7o?9 zX0c3W<9QSD<@Q}yjl1J1OREmpBS4@L0@-wu4hQ|D+gUf_J}yCk009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7dXeFE2WE1s>0c9F7-V)~ByD#%2D009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF{97R3N8Ec6k0Q$d|K%Hi=Xd?@;_?$9K!5-N k0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pkzYAO=Uc7A&hX4Qo diff --git a/TCLSpecs/GUIDrums b/TCLSpecs/GUIDrums deleted file mode 100755 index 38e1945..0000000 --- a/TCLSpecs/GUIDrums +++ /dev/null @@ -1,2 +0,0 @@ -cd .. -wish < TCLSpecs/TCLDrums.tcl | syntmono DrumSynt -r -i diff --git a/TCLSpecs/GUIPhysical b/TCLSpecs/GUIPhysical deleted file mode 100755 index 159f47b..0000000 --- a/TCLSpecs/GUIPhysical +++ /dev/null @@ -1,2 +0,0 @@ -cd .. -wish < TCLSpecs/TCLPhys.tcl | syntmono Clarinet -r -i diff --git a/TCLSpecs/GUIPlukStruk b/TCLSpecs/GUIPlukStruk deleted file mode 100755 index 963625f..0000000 --- a/TCLSpecs/GUIPlukStruk +++ /dev/null @@ -1,2 +0,0 @@ -cd .. -wish < TCLSpecs/TCLStruk.tcl | syntmono Mandolin -r -i diff --git a/TCLSpecs/GUIShakers b/TCLSpecs/GUIShakers deleted file mode 100755 index 4004dcf..0000000 --- a/TCLSpecs/GUIShakers +++ /dev/null @@ -1,2 +0,0 @@ -cd .. -wish < TCLSpecs/TCLShakers.tcl | syntmono Maraca -r -i diff --git a/TCLSpecs/GUIVoice b/TCLSpecs/GUIVoice deleted file mode 100755 index 3892f2d..0000000 --- a/TCLSpecs/GUIVoice +++ /dev/null @@ -1,2 +0,0 @@ -cd .. -wish < TCLSpecs/TCLVoice.tcl | syntmono FMVoices -r -i diff --git a/TubeBell.cpp b/TubeBell.cpp deleted file mode 100644 index ca4c785..0000000 --- a/TubeBell.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************/ -/* Tubular Bell (Orch. Chime) Subclass */ -/* of Algorithm 5 (TX81Z) Subclass of */ -/* 4 Operator FM Synth */ -/* by Perry R. Cook, 1995-96 */ -/******************************************/ - -#include "TubeBell.h" - -TubeBell :: TubeBell() : FM4Alg5() -{ - this->loadWaves("rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw"); - - this->setRatio(0,(MY_FLOAT) (1.0 * 0.995)); - this->setRatio(1,(MY_FLOAT) (1.414 * 0.995)); - this->setRatio(2,(MY_FLOAT) (1.0 * 1.005)); - this->setRatio(3,(MY_FLOAT) (1.414 * 1.000)); - gains[0] = __FM4Op_gains[94]; - gains[1] = __FM4Op_gains[76]; - gains[2] = __FM4Op_gains[99]; - gains[3] = __FM4Op_gains[71]; - adsr[0]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[1]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 2.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[3]->setAllTimes((MY_FLOAT) 0.004,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - twozero->setGain((MY_FLOAT) 0.5); - vibWave->setFreq((MY_FLOAT) 2.0); -} - -void TubeBell :: setFreq(MY_FLOAT frequency) -{ - baseFreq = frequency; - waves[0]->setFreq(baseFreq * ratios[0]); - waves[1]->setFreq(baseFreq * ratios[1]); - waves[2]->setFreq(baseFreq * ratios[2]); - waves[3]->setFreq(baseFreq * ratios[3]); -} - -void TubeBell :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - gains[0] = amp * __FM4Op_gains[94]; - gains[1] = amp * __FM4Op_gains[76]; - gains[2] = amp * __FM4Op_gains[99]; - gains[3] = amp * __FM4Op_gains[71]; - this->setFreq(freq); - this->keyOn(); -#if defined(_debug_) - printf("TubeBell : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} diff --git a/TwoPole.cpp b/TwoPole.cpp deleted file mode 100644 index b344b48..0000000 --- a/TwoPole.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/*******************************************/ -/* Two Pole Filter Class, */ -/* by Perry R. Cook, 1995-96 */ -/* See books on filters to understand */ -/* more about how this works. Nothing */ -/* out of the ordinary in this version. */ -/*******************************************/ - -#include "TwoPole.h" - -TwoPole :: TwoPole() : Filter() -{ - outputs = (MY_FLOAT *) malloc(2 * MY_FLOAT_SIZE); - poleCoeffs[0] = (MY_FLOAT) 0.0; - poleCoeffs[1] = (MY_FLOAT) 0.0; - gain = (MY_FLOAT) 1.0; - this->clear(); -} - -TwoPole :: ~TwoPole() -{ -// free(inputs); - free(outputs); -} - -void TwoPole :: clear() -{ - outputs[0] = (MY_FLOAT) 0.0; - outputs[1] = (MY_FLOAT) 0.0; - lastOutput = (MY_FLOAT) 0.0; -} - -void TwoPole :: setPoleCoeffs(MY_FLOAT *coeffs) -{ - poleCoeffs[0] = coeffs[0]; - poleCoeffs[1] = coeffs[1]; -} - -void TwoPole :: setFreqAndReson(MY_FLOAT freq, MY_FLOAT reson) -{ - poleCoeffs[1] = - (reson * reson); - poleCoeffs[0] = (MY_FLOAT) 2.0 * reson * cos(TWO_PI * (double) freq / SRATE); -} - -void TwoPole :: setGain(MY_FLOAT aValue) -{ - gain = aValue; -} - -MY_FLOAT TwoPole :: tick(MY_FLOAT sample) /* Perform Filter Operation */ -{ /* TwoPole is a two pole filter (duh!) */ - MY_FLOAT temp; /* Look it up in your favorite DSP text */ - temp = sample * gain; - temp += poleCoeffs[0] * outputs[0]; - temp += poleCoeffs[1] * outputs[1]; - outputs[1] = outputs[0]; - outputs[0] = temp; - lastOutput = outputs[0]; - return lastOutput; -} - diff --git a/TwoZero.cpp b/TwoZero.cpp deleted file mode 100644 index 5b9faeb..0000000 --- a/TwoZero.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/*******************************************/ -/* Two Zero Filter Class, */ -/* by Perry R. Cook, 1995-96 */ -/* See books on filters to understand */ -/* more about how this works. Nothing */ -/* out of the ordinary in this version. */ -/*******************************************/ - -#include "TwoZero.h" - -TwoZero :: TwoZero() : Filter() -{ - inputs = (MY_FLOAT *) malloc(2 * MY_FLOAT_SIZE); - zeroCoeffs[0] = (MY_FLOAT) 0.0; - zeroCoeffs[1] = (MY_FLOAT) 0.0; - gain = (MY_FLOAT) 1.0; - this->clear(); -} - -TwoZero :: ~TwoZero() -{ - free(inputs); -} - -void TwoZero :: clear() -{ - inputs[0] = (MY_FLOAT) 0.0; - inputs[1] = (MY_FLOAT) 0.0; - lastOutput = (MY_FLOAT) 0.0; -} - -void TwoZero :: setZeroCoeffs(MY_FLOAT *coeffs) -{ - zeroCoeffs[0] = coeffs[0]; - zeroCoeffs[1] = coeffs[1]; -} - -void TwoZero :: setGain(MY_FLOAT aValue) -{ - gain = aValue; -} - -MY_FLOAT TwoZero :: tick(MY_FLOAT sample) /* Perform Filter Operation */ -{ /* TwoZero is a two zero filter (duh!) */ - /* Look it up in your favorite DSP text */ - lastOutput = zeroCoeffs[0] * inputs[0]; - lastOutput += zeroCoeffs[1] * inputs[1]; - inputs[1] = inputs[0]; - inputs[0] = gain * sample; - lastOutput += inputs[0]; - return lastOutput; -} - diff --git a/WavWvOut.cpp b/WavWvOut.cpp deleted file mode 100644 index 9a5612c..0000000 --- a/WavWvOut.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/*******************************************/ -/* Wave file Output Class, */ -/* by Perry R. Cook, 1995-96 */ -/* This Object opens a DOS/Windows .wav */ -/* 16bit data (signed integers) file, and */ -/* poke buffers of samples into it. */ -/*******************************************/ - -#include "WavWvOut.h" -#include "swapstuf.h" - -/******** Wav Soundfile Header Struct *******/ -struct wavehdr {char riff[4]; - long flength; - char wave[4]; - char fmt[4]; - long format; - short aone; - short numchans; - long srate; - long bps; - short numchans2; - short bps2; - char data[4]; - long dlength; - }; - -FILE *openWAVFile(int chans,char *fileName) { - struct wavehdr hdr = {"RIF",44,"WAV","fmt", - 16,1,1,(long) SRATE,(long) SRATE*2,1,16,"dat",0}; - char tempName[128]; - FILE *fd; - extern int SwapInt(int); - extern short SwapShort(short); - - hdr.riff[3] = 'F'; - hdr.wave[3] = 'E'; - hdr.fmt[3] = ' '; - hdr.data[3] = 'a'; - - strcpy(tempName,fileName); - strcat(tempName,".wav"); - hdr.numchans = chans; - hdr.numchans2 = chans; - hdr.bps = 16; // WARNING!! This may need to be 16 * chans // - hdr.bps2 = 16; - fd = fopen(tempName,"wb"); - if (!fd) { - printf("Couldn't create soundfile %s !!!!!!!!\n",fileName); - exit(0); - } - -#ifndef __LITTLE_ENDIAN__ - hdr.flength = SwapInt(hdr.flength); - hdr.format = SwapInt(hdr.format); - hdr.aone = SwapShort(hdr.aone); - hdr.numchans = SwapShort(hdr.numchans); - hdr.srate = SwapInt(hdr.srate); - hdr.bps = SwapInt(hdr.bps); - hdr.numchans2 = SwapShort(hdr.numchans2); - hdr.bps2 = SwapShort(hdr.bps2); - hdr.dlength = SwapInt(hdr.dlength); -#endif - - printf("Creating soundfile %s.\n", tempName); - fwrite(&hdr,4,11,fd); - return fd; -} - -WavWvOut :: WavWvOut(char *fileName) -{ - chans = 1; - pan = 0.5; - fd = openWAVFile(chans,fileName); - counter = 0; - totalCount = 0; -} - -WavWvOut :: WavWvOut(int channels, char *fileName) -{ - chans = channels; - pan = 0.5; - fd = openWAVFile(chans,fileName); - counter = 0; - totalCount = 0; -} - -WavWvOut :: ~WavWvOut() -{ - MY_FLOAT time; - long bytes; - extern int SwapInt(int); - - fwrite(data,2,counter,fd); - fseek(fd,40,SEEK_SET); - time = (double) totalCount * ONE_OVER_SRATE; - printf("%f Seconds Computed\n",time); - bytes = totalCount*2*chans; -#ifndef __LITTLE_ENDIAN__ - bytes = SwapInt(bytes); -#endif - fwrite(&bytes,4,1,fd); - bytes = totalCount*2*chans + 44; -#ifndef __LITTLE_ENDIAN__ - bytes = SwapInt(bytes); -#endif - fseek(fd,4,SEEK_SET); - fwrite(&totalCount,4,1,fd); - fclose(fd); -} - -long WavWvOut :: getCounter() -{ - return totalCount; -} - -MY_FLOAT WavWvOut :: getTime() -{ - return (MY_FLOAT) totalCount * ONE_OVER_SRATE; -} - -void WavWvOut :: tick(MY_FLOAT sample) -{ - short isample; - extern short SwapShort(short); - - isample = (short) (sample * 32000.0); - if (chans==1) { - data[counter] = isample; -#ifndef __LITTLE_ENDIAN__ - data[counter] = SwapShort(data[counter]); -#endif - counter += 1; - } - else { - data[counter] = (short) (isample * (1.0 - pan)); - data[counter+1] = (short) (isample * pan); -#ifndef __LITTLE_ENDIAN__ - data[counter] = SwapShort(data[counter]); - data[counter+1] = SwapShort(data[counter+1]); -#endif - counter += 2; - } - totalCount += 1; - if (counter == WAV_BUFFER_SIZE) { - fwrite(data,2,WAV_BUFFER_SIZE,fd); - counter = 0; - } -} - -void WavWvOut :: tick(MY_FLOAT lsamp, MY_FLOAT rsamp) -{ - extern short SwapShort(short); - - if (chans==1) { - data[counter] = (short) ((lsamp + rsamp) * 16000.0); -#ifndef __LITTLE_ENDIAN__ - data[counter] = SwapShort(data[counter]); -#endif - counter += 1; - } - else { - data[counter] = (short) (lsamp * 32000.0); - data[counter+1] = (short) (rsamp * 32000.0); -#ifndef __LITTLE_ENDIAN__ - data[counter] = SwapShort(data[counter]); - data[counter+1] = SwapShort(data[counter+1]); -#endif - counter += 2; - } - totalCount += 1; - if (counter == WAV_BUFFER_SIZE) { - fwrite(data,2,WAV_BUFFER_SIZE,fd); - counter = 0; - } -} - -void WavWvOut :: setMonoPan(MY_FLOAT aPan) -{ - pan = aPan; - if (aPan < 0.0) { - pan = 0.0; - printf("Pan < 0.0, correcting to 0.0\n"); - } - if (aPan > 1.0) { - pan = 1.0; - printf("Pan > 1.0, correcting to 1.0\n"); - } -} diff --git a/Wurley.cpp b/Wurley.cpp deleted file mode 100644 index 5783acb..0000000 --- a/Wurley.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************/ -/* Wurlitzer Electric Piano Subclass */ -/* of Algorithm 5 (TX81Z) Subclass of */ -/* 4 Operator FM Synth */ -/* by Perry R. Cook, 1995-96 */ -/******************************************/ - -#include "Wurley.h" - -Wurley :: Wurley() : FM4Alg5() -{ - this->loadWaves("rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/sinewave.raw", - "rawwaves/fwavblnk.raw"); - - this->setRatio(0,(MY_FLOAT) 1.0); - this->setRatio(1,(MY_FLOAT) 4.0); - this->setRatio(2,(MY_FLOAT) -510.0); - this->setRatio(3,(MY_FLOAT) -510.0); - gains[0] = __FM4Op_gains[99]; - gains[1] = __FM4Op_gains[82]; - gains[2] = __FM4Op_gains[92]; - gains[3] = __FM4Op_gains[68]; /* Originally 78, but sounded stinky */ - twozero->setGain((MY_FLOAT) 2.0); - adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.25,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - adsr[3]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.15,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04); - vibWave->setFreq((MY_FLOAT) 8.0); -} - -void Wurley :: setFreq(MY_FLOAT frequency) -{ - baseFreq = frequency; - waves[0]->setFreq(baseFreq * ratios[0]); - waves[1]->setFreq(baseFreq * ratios[1]); - waves[2]->setFreq(ratios[2]); /* Note here a 'fixed resonance' */ - waves[3]->setFreq(ratios[3]); -} - -void Wurley :: noteOn(MY_FLOAT freq, MY_FLOAT amp) -{ - gains[0] = amp * __FM4Op_gains[99]; - gains[1] = amp * __FM4Op_gains[82]; - gains[2] = amp * __FM4Op_gains[82]; /* Originally 92 */ - gains[3] = amp * __FM4Op_gains[68]; /* Originally 78 */ - this->setFreq(freq); - this->keyOn(); -#if defined(_debug_) - printf("Wurley : NoteOn: Freq=%lf Amp=%lf\n",freq,amp); -#endif -} diff --git a/WvOut.cpp b/WvOut.cpp deleted file mode 100644 index 996cdac..0000000 --- a/WvOut.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/*******************************************/ -/* Output Abstract Class, */ -/* by Tim Stilson, 1996 */ -/* based on code by Perry R. Cook, 1995-96*/ -/*******************************************/ - -#include "WvOut.h" -#include - -WvOut :: WvOut() -{ -} - -WvOut :: ~WvOut() -{ -} - -void WvOut :: tick(MY_FLOAT sample) -{ -} - diff --git a/WvOut.h b/WvOut.h deleted file mode 100644 index 7b402e6..0000000 --- a/WvOut.h +++ /dev/null @@ -1,23 +0,0 @@ -/********************************************/ -/* Output Abstract Class, */ -/* by Tim Stilson, 1996 */ -/* based on code by Perry R. Cook, 1995-96 */ -/********************************************/ - -#include "Object.h" - -#if !defined(__WvOut_h) -#define __WvOut_h - - - -class WvOut : public Object -{ - public: - WvOut(); - virtual ~WvOut(); - virtual void tick(MY_FLOAT sample); -}; - - -#endif // defined(__WvOut_h) diff --git a/effects/Chorus.cpp b/effects/Chorus.cpp new file mode 100644 index 0000000..1c346e2 --- /dev/null +++ b/effects/Chorus.cpp @@ -0,0 +1,125 @@ +/******************************************/ +/* Chorus Effect Applied to Soundfile */ +/* by Perry Cook, 1996 */ +/******************************************/ + +#include "Chorus.h" + +Chorus :: Chorus(MY_FLOAT baseDelay) +{ + delayLine[0] = new DLineL((long) (baseDelay * 1.414) + 2); + delayLine[1] = new DLineL((long) (baseDelay) + 2); + delayLine[0]->setDelay(baseDelay); + delayLine[1]->setDelay(baseDelay); + baseLength = baseDelay; + + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char temp[128]; + strcpy(temp, RAWWAVE_PATH); + mods[0] = new RawWvIn(strcat(temp,"rawwaves/sinewave.raw"),"looping"); + strcpy(temp, RAWWAVE_PATH); + mods[1] = new RawWvIn(strcat(temp,"rawwaves/sinewave.raw"),"looping"); + mods[0]->normalize(); + mods[1]->normalize(); + mods[0]->setFreq(0.2); + mods[1]->setFreq(0.222222); + modDepth = 0.05; + effectMix = (MY_FLOAT) 0.5; + this->clear(); +} + +Chorus :: ~Chorus() +{ + delete delayLine[0]; + delete delayLine[1]; + delete mods[0]; + delete mods[1]; +} + +void Chorus :: clear() +{ + delayLine[0]->clear(); + delayLine[1]->clear(); + lastOutL = (MY_FLOAT) 0.0; + lastOutR = (MY_FLOAT) 0.0; +} + +void Chorus :: setEffectMix(MY_FLOAT mix) +{ + effectMix = mix; +} + +void Chorus :: setModDepth(MY_FLOAT depth) +{ + modDepth = depth; +} + +void Chorus :: setModFreq(MY_FLOAT freq) +{ + mods[0]->setFreq(freq); + mods[1]->setFreq(freq*1.1111); +} + +MY_FLOAT Chorus :: lastOutput() +{ + return (lastOutL + lastOutR) * (MY_FLOAT) 0.5; +} + +MY_FLOAT Chorus :: lastOutputL() +{ + return lastOutL; +} + +MY_FLOAT Chorus :: lastOutputR() +{ + return lastOutR; +} + +MY_FLOAT Chorus :: tick(MY_FLOAT input) +{ + delayLine[0]->setDelay(baseLength * 0.707 * (1.0 + mods[0]->tick())); + delayLine[1]->setDelay(baseLength * 0.5 * (1.0 - mods[1]->tick())); + lastOutL = input * (1.0 - effectMix); + lastOutL += effectMix * delayLine[0]->tick(input); + lastOutR = input * (1.0 - effectMix); + lastOutR += effectMix * delayLine[1]->tick(input); + return (lastOutL + lastOutR) * (MY_FLOAT) 0.5; +} + +/************** Test Main Program *********************/ +/* +int main(int argc, char *argv[]) +{ + FILE *soundIn,*soundOut; + short data; + float efMix,maxDel; + Chorus *effect; + + if (argc==5) { + soundIn = fopen(argv[3],"rb"); + soundOut = fopen(argv[4],"wb"); + if (soundIn && soundOut) { + efMix = atof(argv[1]); + maxDel = atof(argv[2]); + effect = new Chorus(maxDel); + effect->setEffectMix(efMix); + while (fread(&data,2,1,soundIn)) { + data = effect->tick(data); + fwrite(&data,2,1,soundOut); + } + delete effect; + fclose(soundIn); + fclose(soundOut); + } + else { + printf("Can't open one of the files\n"); + } + } + else { + printf("useage: Chorus mix maxDelay soundIn.snd soundOut.snd\n"); + printf("0.0 <= mix <= 1.0\n"); + printf("maxDelay is in samples\n"); + printf("soundfiles are 16 bit linear mono or stereo\n"); + } +} +*/ diff --git a/effects/Chorus.h b/effects/Chorus.h new file mode 100644 index 0000000..cf69163 --- /dev/null +++ b/effects/Chorus.h @@ -0,0 +1,37 @@ +/******************************************/ +/* Chorus Effect */ +/* by Perry Cook, 1996 */ +/******************************************/ + +#if !defined(__Chorus_h) +#define __Chorus_h + +#include "../STK/Object.h" +#include "../STK/DLineL.h" +#include "../STK/RawWvIn.h" + +class Chorus : public Object +{ + protected: + DLineL *delayLine[2]; + RawWvIn *mods[2]; + MY_FLOAT baseLength; + MY_FLOAT modDepth; + MY_FLOAT lastOutL; + MY_FLOAT lastOutR; + MY_FLOAT effectMix; + public: + Chorus(MY_FLOAT baseDelay); + ~Chorus(); + void clear(); + void setModDepth(MY_FLOAT depth); + void setModFreq(MY_FLOAT freq); + void setEffectMix(MY_FLOAT mix); + MY_FLOAT lastOutput(); + MY_FLOAT lastOutputL(); + MY_FLOAT lastOutputR(); + MY_FLOAT tick(MY_FLOAT input); +}; + +#endif + diff --git a/effects/Debug/.placeholder b/effects/Debug/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/effects/Echo.cpp b/effects/Echo.cpp new file mode 100644 index 0000000..0e1378f --- /dev/null +++ b/effects/Echo.cpp @@ -0,0 +1,44 @@ +/******************************************/ +/* Echo Effect */ +/* by Perry Cook, 1996 */ +/******************************************/ + +#include "Echo.h" + +Echo :: Echo(MY_FLOAT longestDelay) +{ + length = (long) longestDelay + 2; + delayLine = new DLineN(length); + effectMix = 0.5; + this->clear(); + this->setDelay(longestDelay); +} + +Echo :: ~Echo() +{ + delete delayLine; +} + +void Echo :: clear() +{ + delayLine->clear(); + lastOut = 0.0; +} + +void Echo :: setDelay(MY_FLOAT delay) +{ + delayLine->setDelay(delay); +} + +void Echo :: setEffectMix(MY_FLOAT mix) +{ + effectMix = mix; +} + +MY_FLOAT Echo :: tick(MY_FLOAT input) +{ + lastOut = effectMix * delayLine->tick(input); + lastOut += input * (1.0 - effectMix); + return lastOut; +} + diff --git a/effects/Echo.h b/effects/Echo.h new file mode 100644 index 0000000..8a211d6 --- /dev/null +++ b/effects/Echo.h @@ -0,0 +1,30 @@ +/******************************************/ +/* Echo Effect Applied to Soundfile */ +/* by Perry Cook, 1996 */ +/******************************************/ + +#if !defined(__Echo_h) +#define __Echo_h + +#include "../STK/Object.h" +#include "../STK/DLineN.h" + +class Echo : public Object +{ + protected: + DLineN *delayLine; + long length; + MY_FLOAT lastOut; + MY_FLOAT effectMix; + public: + Echo(MY_FLOAT longestDelay); + ~Echo(); + void clear(); + void setDelay(MY_FLOAT delay); + void setEffectMix(MY_FLOAT mix); + MY_FLOAT lastOutput(); + MY_FLOAT tick(MY_FLOAT input); +}; + +#endif + diff --git a/effects/GUIeffects b/effects/GUIeffects new file mode 100755 index 0000000..8d3a494 --- /dev/null +++ b/effects/GUIeffects @@ -0,0 +1 @@ +wish < tcl/Effects.tcl | effects -ip diff --git a/effects/Makefile b/effects/Makefile new file mode 100644 index 0000000..63239f3 --- /dev/null +++ b/effects/Makefile @@ -0,0 +1,60 @@ +# Effects Makefile + +OS = $(shell uname) + +# The following definition indicates the relative location of +# the core STK classes. +STK_PATH = ../STK/ + +O_FILES = Object.o Reverb.o PRCRev.o JCRev.o \ + NRev.o RTSoundIO.o DLineN.o Filter.o \ + RTDuplex.o SKINI11.o Envelope.o Echo.o \ + PitShift.o DLineL.o Chorus.o RawWvIn.o \ + WvIn.o swapstuf.o threads.o + + +RM = /bin/rm + +ifeq ($(OS),IRIX) # These are for SGI + INSTR = effects + CC = CC -O2 -D__OS_IRIX_ # -g -fullwarn -D__SGI_CC__ + LIBRARY = -L/usr/sgitcl/lib -laudio -lmd -lm +endif + +ifeq ($(OS),Linux) # These are for Linux + INSTR = effects + CC = g++ -O3 -D__OS_Linux_ # -g + LIBRARY = -lpthread -lm +endif + +%.o : $(STK_PATH)%.cpp + $(CC) -c $(<) -o $@ + +all: $(INSTR) + +clean : + rm *.o + rm $(INSTR) + +cleanIns : + rm $(INSTR) + +strip : + strip $(INSTR) + +effects: effects.cpp $(O_FILES) + $(CC) -O3 -o effects effects.cpp $(O_FILES) $(LIBRARY) + +# $(O_FILES) : + +threads.o: threads.cpp + $(CC) -c threads.cpp + +Echo.o: Echo.cpp + $(CC) -c Echo.cpp + +PitShift.o: PitShift.cpp + $(CC) -c PitShift.cpp + +Chorus.o: Chorus.cpp + $(CC) -c Chorus.cpp \ No newline at end of file diff --git a/effects/PitShift.cpp b/effects/PitShift.cpp new file mode 100644 index 0000000..6859925 --- /dev/null +++ b/effects/PitShift.cpp @@ -0,0 +1,106 @@ +/*********************************************/ +/* PitchShift Effect */ +/* by Perry Cook, 1996 */ +/*********************************************/ + +#include "PitShift.h" + +PitShift :: PitShift() +{ + delayLine[0] = new DLineL((long) 1024); + delayLine[1] = new DLineL((long) 1024); + delay[0] = 12; + delay[1] = 512; + delayLine[0]->setDelay(delay[0]); + delayLine[1]->setDelay(delay[1]); + effectMix = (MY_FLOAT) 0.5; + rate = 1.0; +} + +PitShift :: ~PitShift() +{ + delete delayLine[0]; + delete delayLine[1]; +} + +void PitShift :: setEffectMix(MY_FLOAT mix) +{ + effectMix = mix; +} + +void PitShift :: setShift(MY_FLOAT shift) +{ + if (shift < 1.0) { + rate = 1.0 - shift; + } + else if (shift > 1.0) { + rate = 1.0 - shift; + } + else { + rate = 0.0; + delay[0] = 512; + } +} + +MY_FLOAT PitShift :: lastOutput() +{ + return lastOut; +} + +MY_FLOAT PitShift :: tick(MY_FLOAT input) +{ + delay[0] = delay[0] + rate; + while (delay[0] > 1012) delay[0] -= 1000; + while (delay[0] < 12) delay[0] += 1000; + delay[1] = delay[0] + 500; + while (delay[1] > 1012) delay[1] -= 1000; + while (delay[1] < 12) delay[1] += 1000; + delayLine[0]->setDelay(delay[0]); + delayLine[1]->setDelay(delay[1]); + env[1] = fabs(delay[0] - 512) * 0.002; + env[0] = 1.0 - env[1]; + lastOut = env[0] * delayLine[0]->tick(input); + lastOut += env[1] * delayLine[1]->tick(input); + lastOut *= effectMix; + lastOut += (1.0 - effectMix) * input; + return lastOut; +} + +/************** Test Main Program *********************/ +/* +int main(int argc, char *argv[]) +{ + FILE *soundIn,*soundOut; + short data; + float efMix,pitchshift; + PitShift *effect; + + if (argc==5) { + soundIn = fopen(argv[3],"rb"); + soundOut = fopen(argv[4],"wb"); + if (soundIn && soundOut) { + efMix = atof(argv[1]); + pitchshift = atof(argv[2]); + effect = new PitShift(); + effect->setShift(pitchshift); + effect->setEffectMix(efMix); + while (fread(&data,2,1,soundIn)) { + data = effect->tick(data); + fwrite(&data,2,1,soundOut); + } + delete effect; + fclose(soundIn); + fclose(soundOut); + } + else { + printf("Can't open one of the files\n"); + } + } + else { + printf("useage: pitshift mix shiftRate soundIn.snd soundOut.snd\n"); + printf("0.0 <= mix <= 1.0\n"); + printf("maxDelay is in samples\n"); + printf("soundfiles are 16 bit linear mono or stereo\n"); + } +} +*/ diff --git a/effects/PitShift.h b/effects/PitShift.h new file mode 100644 index 0000000..080246c --- /dev/null +++ b/effects/PitShift.h @@ -0,0 +1,32 @@ +/*********************************************/ +/* PitchShift Effect */ +/* by Perry Cook, 1996 */ +/*********************************************/ + +#if !defined(__PitShift_h) +#define __PitShift_h + +#include "../STK/Object.h" +#include "../STK/DLineL.h" + +class PitShift : public Object +{ + protected: + DLineL *delayLine[2]; + MY_FLOAT lastOut; + MY_FLOAT delay[2]; + MY_FLOAT env[2]; + MY_FLOAT effectMix; + MY_FLOAT rate; + public: + PitShift(); + ~PitShift(); + void clear(); + void setShift(MY_FLOAT shift); + void setEffectMix(MY_FLOAT mix); + virtual MY_FLOAT lastOutput(); + MY_FLOAT tick(MY_FLOAT input); +}; + +#endif + diff --git a/effects/README-effects.txt b/effects/README-effects.txt new file mode 100644 index 0000000..1e6141b --- /dev/null +++ b/effects/README-effects.txt @@ -0,0 +1,16 @@ +STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Version 3.0 + +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99. + + +EFFECTS PROJECT: + +This directory contains a program that demonstrates realtime duplex mode (simultaneous audio input and output) operation, as well as several simple delay-line based effects algorithms. Proper duplex mode operation is very hardware dependent. If you have trouble with this application, make sure your soundcard supports the desired sample rates and sample sizes. Under Linux, also make sure that your soundcard is supported by OSS (http://www.opensound.com/). + +NOTES: + +1. This project will not run under WindowsNT or NeXTStep, due to lack of realtime audio input support. + +2. Audio input from either a microphone or line-input device MUST be available to the audio input port when the program is started. \ No newline at end of file diff --git a/effects/Release/.placeholder b/effects/Release/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/effects/effects.cpp b/effects/effects.cpp new file mode 100644 index 0000000..70ebbf1 --- /dev/null +++ b/effects/effects.cpp @@ -0,0 +1,159 @@ +/************** Test Effects Program *********************/ + +#include "../STK/RTSoundIO.h" +#include "../STK/RTDuplex.h" +#include "../STK/SKINI11.h" +#include "../STK/SKINI11.msg" +#include "../STK/Envelope.h" +#include "../STK/PRCRev.h" +#include "../STK/JCRev.h" +#include "../STK/NRev.h" +#include "Echo.h" +#include "PitShift.h" +#include "Chorus.h" + +// The input command pipe and socket threads are defined in threads.cpp. +#include "threads.h" + +int numStrings = 0; +int notDone = 1; +char **inputString; + +void errorfun(void) { + /* Error function in case of incorrect command-line argument specifications */ + printf("\nuseage: effects flag \n"); + printf(" where flag = -ip for realtime SKINI input by pipe\n"); + printf(" (won't work under Win95/98),\n"); + printf(" and flag = -is for realtime SKINI input by socket.\n"); + exit(0); +} + +void main(int argc,char *argv[]) +{ + MY_FLOAT inSample = 0.0; + MY_FLOAT lastSample = 0.0; + MY_FLOAT byte3; + long i, synlength; + int type, j, outOne = 0, effect = 0, useSocket = 0; + + if (argc != 2) errorfun(); + + if (!strcmp(argv[1],"-is") ) + useSocket = 1; + else if (strcmp(argv[1],"-ip")) { + errorfun(); + } + + RTDuplex *inout = new RTDuplex(SRATE,1); + Echo *echo = new Echo(SRATE); // one second delay + PitShift *shifter = new PitShift(); + Chorus *chorus = new Chorus(5000.0); + PRCRev *prcrev = new PRCRev(2.0); + JCRev *jcrev = new JCRev(2.0); + NRev *nrev = new NRev(2.0); + SKINI11 *score = new SKINI11(); + Envelope *envelope = new Envelope; + + // Start the input thread + if (useSocket) + startSocketThread(); + else + startPipeThread(); + + /* Finally ... the runtime loop begins! */ + notDone = 1; + synlength = RT_BUFFER_SIZE; + while(notDone || numStrings) { + if (numStrings > 1) synlength = (long) RT_BUFFER_SIZE / numStrings; + else synlength = RT_BUFFER_SIZE; + for ( i=0; itick(envelope->tick() * echo->tick(lastSample)); + else if (effect == 1) + inSample = inout->tick(envelope->tick() * shifter->tick(lastSample)); + else if (effect == 2) + inSample = inout->tick(envelope->tick() * chorus->tick(lastSample)); + else if (effect == 3) + inSample = inout->tick(envelope->tick() * prcrev->tick(lastSample)); + else if (effect == 4) + inSample = inout->tick(envelope->tick() * jcrev->tick(lastSample)); + else if (effect == 5) + inSample = inout->tick(envelope->tick() * nrev->tick(lastSample)); + lastSample = inSample; + } + if (numStrings) { + score->parseThis(inputString[outOne]); + type = score->getType(); + if (type > 0) { + if (type == __SK_NoteOn_ ) { + if (( byte3 = score->getByteThree() ) == 0) { // NoteOff + envelope->setRate(0.001); + envelope->setTarget(0.0); + } + else { // Really a NoteOn + envelope->setRate(0.001); + envelope->setTarget(1.0); + } + } + else if (type == __SK_NoteOff_) { + envelope->setRate(0.001); + envelope->setTarget(0.0); + } + else if (type == __SK_ControlChange_) { + j = (int) score->getByteTwo(); + byte3 = score->getByteThree(); + if (j == 20) effect = (int) byte3; // effect change + else if (j == 21) { // effects mix + echo->setEffectMix(byte3*NORM_7); + shifter->setEffectMix(byte3*NORM_7); + chorus->setEffectMix(byte3*NORM_7); + prcrev->setEffectMix(byte3*NORM_7); + jcrev->setEffectMix(byte3*NORM_7); + nrev->setEffectMix(byte3*NORM_7); + } + else if (j == 22) { // effect1 parameter change + echo->setDelay(byte3*NORM_7*SRATE*0.95 + 2); + shifter->setShift(byte3*NORM_7*3 + 0.25); + chorus->setModFreq(byte3*NORM_7); + } + else if (j == 23) { // effect1 parameter change + chorus->setModDepth(byte3*NORM_7*0.2); + } + } + } + outOne += 1; + if (outOne == MAX_IN_STRINGS) outOne = 0; + numStrings--; + } + } + + envelope->setRate(0.001); + envelope->setTarget(0.0); + for (i=0;itick(envelope->tick() * echo->tick(lastSample)); + else if (effect == 1) + inSample = inout->tick(envelope->tick() * shifter->tick(lastSample)); + else if (effect == 2) + inSample = inout->tick(envelope->tick() * chorus->tick(lastSample)); + else if (effect == 3) + inSample = inout->tick(envelope->tick() * prcrev->tick(lastSample)); + else if (effect == 4) + inSample = inout->tick(envelope->tick() * jcrev->tick(lastSample)); + else if (effect == 5) + inSample = inout->tick(envelope->tick() * nrev->tick(lastSample)); + lastSample = inSample; + } + + delete inout; + delete echo; + delete shifter; + delete chorus; + delete prcrev; + delete jcrev; + delete nrev; + delete score; + delete envelope; + + printf("effects finished ... goodbye.\n"); +} diff --git a/effects/effects.dsp b/effects/effects.dsp new file mode 100644 index 0000000..e5d530d --- /dev/null +++ b/effects/effects.dsp @@ -0,0 +1,243 @@ +# Microsoft Developer Studio Project File - Name="effects" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=effects - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "effects.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "effects.mak" CFG="effects - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "effects - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "effects - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "effects - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__OS_Win_" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Wsock32.lib dsound.lib /nologo /subsystem:console /machine:I386 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "effects - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__OS_Win_" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Wsock32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "effects - Win32 Release" +# Name "effects - Win32 Debug" +# Begin Source File + +SOURCE=.\Chorus.cpp +# End Source File +# Begin Source File + +SOURCE=.\Chorus.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\DLineL.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\DLineL.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\DLineN.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\DLineN.h +# End Source File +# Begin Source File + +SOURCE=.\Echo.cpp +# End Source File +# Begin Source File + +SOURCE=.\Echo.h +# End Source File +# Begin Source File + +SOURCE=.\effects.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\Envelope.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\Envelope.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\Filter.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\Filter.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\JCRev.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\JCRev.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\NRev.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\NRev.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\Object.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\Object.h +# End Source File +# Begin Source File + +SOURCE=.\PitShift.cpp +# End Source File +# Begin Source File + +SOURCE=.\PitShift.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\PRCRev.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\PRCRev.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\RawWvIn.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\RawWvIn.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\Reverb.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\Reverb.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\RTDuplex.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\RTDuplex.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\RTSoundIO.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\RTSoundIO.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\SKINI11.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\SKINI11.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\swapstuf.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\swapstuf.h +# End Source File +# Begin Source File + +SOURCE=.\threads.cpp +# End Source File +# Begin Source File + +SOURCE=.\threads.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\WvIn.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\WvIn.h +# End Source File +# End Target +# End Project diff --git a/effects/effects.dsw b/effects/effects.dsw new file mode 100644 index 0000000..135e27f --- /dev/null +++ b/effects/effects.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "effects"=.\effects.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/effects/effects.opt b/effects/effects.opt new file mode 100644 index 0000000000000000000000000000000000000000..c79140393840425f0d68b251fd347516e7c4de7b GIT binary patch literal 48640 zcmeI5Npl-T7=~MpoybY-D2Zc-eG;-k7D&PxAUKv{P>5q=6B~jlYAm&F;qi=WX6#^2 z=D>}8D~{nnapo70Dt-VQIff$?#UG%8dB0g?$#_!?#aQ*W>YZs#f7AWV)2%st)!q8( z$KAKS{_gHy)RJ^wZBc(Mw5qLD*Hg%gf|OAxUs#ZQe3m>eF>N9Ox8bXSe%k^yLoJZ> z`8FsGwL;sW9gy^M8_w;}E(k+F?S}S1d!Y_!A0&2NIPZt}@uHrlljX9zN}fM99YO1^ zno^!BD^nGeh9^Vt!-RBfw945~O~DwIu`7P8Vp=H*{FK3p)PvR?bsg<%dzH2&t*4GoF^bwxq5s79ZF+oM7kuwf_T1>C zGp$D_Om}Y7^QT6mg7|!@=#}LLaQ`oo`{}PwibK=TFq-H@J9}`fO5Kvr;4?27aYJ52 zMv%ub@@rmlY1M5=p(urK8JKk=Uw?}Innd+~K=OVJ-;VXywdx%ydFwXg0|4EtS_Xwy z#A^oz;d>CqgW4Iqq1V2*K8t1iYRaUWr59530siXx%8_5+h)?)&{u{iS-<|J9Ya2>2 zuY2H=3GD>0?Ksw-GDhW|(63A?g+5~<0TLhq5+DH*AOR8}0TLjAgc3+Ks~bOmdtJSs ze=DIL(k&8bECL@Wb)(!~QYX~p|N3Ab|2~Vjehr1ud0Ycv8$ivKOQ{(1x}l5MO@mUYoo?=J*Sr4t_%iLUzr|Y1 z?RU`r<5lfvm4&tL9zMgMu0h3PE?eZA$`i~WP= ztB<>)W#C3ZY_u1aEq^uLTe9?GH3vo6bj2G+hev|Fx*blx6!7Tu_Rnmsu^t1YLjU1Pv?iZfXF9u}`y5Ed_avS8x>*e+c!(L zcB5Kb(jB==Bh|-c1A~nJasH}@BWXkRJAYJji!3{pWJABOYh%E z``hvVKVjOBQ;SM*Rf7!<+@A-P2*}?e6`2b=2TpQb=vd$a;9(kb7gJxl|0ib zP1w`g_T@ei`sHTkLZP5N&loN_mg$!qJ1*Rc!nV9^444zzxahi$8<%vUr1gqp^yz8G zor|yDhpT(@mZc|5%gEcF?_%f1s`?J2eE${4F5n4D_KZ1Zq1v|#x?F8^B&fumg{ws) zSMth^SE??5ALaWlxUM;8^f)ti(F>~3MO}>D!0-2FXRqa$zLC?m<4(hye(WV_+7*)z zVbT$qlx9msZH&zs*R<=zb$qmFkhk^QX2HjbdvC_7fcqE*U11IR$l0KcYF9sjO?zn5 zKdaqw%efxc|0#-gheboaS(q}ile#b!mwtxQU18}}EQ_zX^K+DS=51e3V0X;ivd(`2 zlT2)~wB;}5{s9*^qMvkexqiA0llIW9rFTD`!jQ;azHD4^e4|(R$3FL<_%N(I;PoN?dC9zXui5g?" with flags "" + +Creating temp file "C:\WINDOWS\TEMP\RSP1050.TMP" with contents +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSP1050.TMP" +Creating temp file "C:\WINDOWS\TEMP\RSP1051.TMP" with contents +Creating command line "link.exe @C:\WINDOWS\TEMP\RSP1051.TMP" +Compiling... +Chorus.cpp +DLineL.cpp +DLineN.cpp +Echo.cpp +effects.cpp +Envelope.cpp +Filter.cpp +JCRev.cpp +NRev.cpp +Object.cpp +PitShift.cpp +PRCRev.cpp +RawWvIn.cpp +Reverb.cpp +RTDuplex.cpp +RTSoundIO.cpp +SKINI11.cpp +swapstuf.cpp +WvIn.cpp +Linking... + + + +effects.exe - 0 error(s), 0 warning(s) diff --git a/effects/tcl/Effects.tcl b/effects/tcl/Effects.tcl new file mode 100644 index 0000000..9c37240 --- /dev/null +++ b/effects/tcl/Effects.tcl @@ -0,0 +1,219 @@ +set mixlevel 64.0 +set effect1 64.0 +set effect2 64.0 +set effect 0 +set outID "stdout" +set commtype "stdout" + +# Configure main window +wm title . "STK Effects Controller" +wm iconname . "Effects" +. config -bg black + +# Configure "communications" menu +menu .menu -tearoff 0 +menu .menu.communication -tearoff 0 +.menu add cascade -label "Communication" -menu .menu.communication \ + -underline 0 +.menu.communication add radio -label "Console" -variable commtype \ + -value "stdout" -command { setComm } +.menu.communication add radio -label "Socket" -variable commtype \ + -value "socket" -command { setComm } +. configure -menu .menu + +# Configure title display +label .title -text "STK Effects Controller" \ + -font {Times 14 bold} -background white \ + -foreground darkred -relief raised + +label .title2 -text "by Gary P. Scavone\n Center for Computer Research in Music & Acoustics (CCRMA) \n Stanford University" \ + -font {Times 12 bold} -background white \ + -foreground darkred -relief raised + +pack .title -padx 5 -pady 10 +pack .title2 -padx 5 -pady 10 + +# Configure "note-on" buttons +frame .noteOn -bg black + +button .noteOn.on -text NoteOn -bg grey66 -command { noteOn 64.0 64.0 } +button .noteOn.off -text NoteOff -bg grey66 -command { noteOff 64.0 127.0 } +button .noteOn.exit -text "Exit Program" -bg grey66 -command myExit +pack .noteOn.on -side left -padx 5 +pack .noteOn.off -side left -padx 5 -pady 10 +pack .noteOn.exit -side left -padx 5 -pady 10 + +pack .noteOn + +# Configure sliders +frame .left -bg black + +scale .left.effectsmix -from 0 -to 127 -length 400 \ +-command {printWhatz "ControlChange 0.0 1 " 21} \ +-orient horizontal -label "Effects Mix" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable mixlevel + +scale .left.effect1 -from 0 -to 127 -length 400 \ +-command {printWhatz "ControlChange 0.0 1 " 22} \ +-orient horizontal -label "Echo Delay" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable effect1 + +scale .left.effect2 -from 0 -to 127 -length 400 \ +-command {printWhatz "ControlChange 0.0 1 " 23} \ +-orient horizontal -label "Disabled" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable effect2 + +pack .left.effectsmix -padx 10 -pady 3 +pack .left.effect1 -padx 10 -pady 3 +pack .left.effect2 -padx 10 -pady 3 + +pack .left -side left + +# Configure effect select buttons +frame .effectSelect -bg black +pack .effectSelect -side right -padx 5 -pady 5 + +radiobutton .effectSelect.echo -text "Echo" -variable effect -relief flat \ + -value 0 -command {changeEffect "ControlChange 0.0 1 " 20 $effect} +radiobutton .effectSelect.shifter -text "Pitch Shift" -variable effect -relief flat \ + -value 1 -command {changeEffect "ControlChange 0.0 1 " 20 $effect} +radiobutton .effectSelect.chorus -text "Chorus" -variable effect -relief flat \ + -value 2 -command {changeEffect "ControlChange 0.0 1 " 20 $effect} +radiobutton .effectSelect.prcrev -text "PRC Reverb" -variable effect -relief flat \ + -value 3 -command {changeEffect "ControlChange 0.0 1 " 20 $effect} +radiobutton .effectSelect.jcrev -text "JC Reverb" -variable effect -relief flat \ + -value 4 -command {changeEffect "ControlChange 0.0 1 " 20 $effect} +radiobutton .effectSelect.nrev -text "NRev Reverb" -variable effect -relief flat \ + -value 5 -command {changeEffect "ControlChange 0.0 1 " 20 $effect} + +pack .effectSelect.echo -pady 2 -padx 5 -side top -anchor w -fill x +pack .effectSelect.shifter -pady 2 -padx 5 -side top -anchor w -fill x +pack .effectSelect.chorus -pady 2 -padx 5 -side top -anchor w -fill x +pack .effectSelect.prcrev -pady 2 -padx 5 -side top -anchor w -fill x +pack .effectSelect.jcrev -pady 2 -padx 5 -side top -anchor w -fill x +pack .effectSelect.nrev -pady 2 -padx 5 -side top -anchor w -fill x + + +proc myExit {} { + global outID + puts $outID [format "NoteOff 0.0 1 64 127" ] + flush $outID + puts $outID [format "ExitProgram"] + flush $outID + close $outID + exit +} + +proc noteOn {pitchVal pressVal} { + global outID + puts $outID [format "NoteOn 0.0 1 %f %f" $pitchVal $pressVal] + flush $outID +} + +proc noteOff {pitchVal pressVal} { + global outID + puts $outID [format "NoteOff 0.0 1 %f %f" $pitchVal $pressVal] + flush $outID +} + +proc printWhatz {tag value1 value2 } { + global outID + puts $outID [format "%s %i %f" $tag $value1 $value2] + flush $outID +} + +proc changeEffect {tag value1 value2 } { + global outID + if ($value2==0) { + .left.effect1 config -state normal -label "Echo Delay" + .left.effect2 config -state disabled -label "Disabled" + } + if ($value2==1) { + .left.effect1 config -state normal -label "Pitch Shift Amount" + .left.effect2 config -state disabled -label "Disabled" + } + if ($value2==2) { + .left.effect1 config -state normal -label "Chorus Modulation Frequency" + .left.effect2 config -state normal -label "Chorus Modulation Depth" + } + if {$value2>=3 && $value2<=5} { + .left.effect1 config -state disabled -label "Disabled" + .left.effect2 config -state disabled -label "Disabled" + } + puts $outID [format "%s %i %f" $tag $value1 $value2] + flush $outID +} + +# Bind an X windows "close" event with the Exit routine +bind . +myExit + +# Socket connection procedure +set d .socketdialog + +proc setComm {} { + global outID + global commtype + global d + if {$commtype == "stdout"} { + if { [string compare "stdout" $outID] } { + set i [tk_dialog .dialog "Break Socket Connection?" {You are about to break an existing socket connection ... is this what you want to do?} "" 0 Cancel OK] + switch $i { + 0 {set commtype "socket"} + 1 {close $outID + set outID "stdout"} + } + } + } elseif { ![string compare "stdout" $outID] } { + set sockport 2001 + set sockhost localhost + toplevel $d + wm title $d "STK Client Socket Connection" + wm resizable $d 0 0 + grab $d + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + -background white -font {Helvetica 10 bold} \ + -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} + frame $d.sockport + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ + -font {Helvetica 10 bold} + pack $d.message -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost + $d.sockport.entry insert 0 $sockport + frame $d.buttons + button $d.buttons.cancel -text "Cancel" -bg grey66 \ + -command { set commtype "stdout" + set outID "stdout" + destroy $d } + button $d.buttons.connect -text "Connect" -bg grey66 \ + -command { + set sockhost [$d.sockhost.entry get] + set sockport [$d.sockport.entry get] + set err [catch {socket $sockhost $sockport} outID] + + if {$err == 0} { + destroy $d + } else { + tk_dialog $d.error "Socket Error" {Error: Unable to make socket connection. Make sure the STK socket server is first running and that the port number is correct.} "" 0 OK + } } + pack $d.buttons.cancel -side left -padx 5 -pady 10 + pack $d.buttons.connect -side right -padx 5 -pady 10 + pack $d.buttons -side bottom -padx 5 -pady 10 + } +} + + + diff --git a/effects/threads.cpp b/effects/threads.cpp new file mode 100644 index 0000000..7e7d640 --- /dev/null +++ b/effects/threads.cpp @@ -0,0 +1,282 @@ +// Thread functions for use with syntmono. +// +// Gary P. Scavone, 1999. + +#include "threads.h" + +#if defined(__STK_REALTIME_) + +#define SERVICE_PORT 2001 // Socket Port ID number + +// Do OS dependent declarations and includes +#if defined(__OS_IRIX_) +#include +#include +#include +#include +#include +#include +#include + +pid_t string_thread; + +#elif defined(__OS_Linux_) +#include +#include +#include +#include +#include +#include + +pthread_t string_thread; + +#elif defined(__OS_Win_) +#include +#include + +unsigned long string_thread; + +#endif + + +// The thread function definition protocols are slightly +// different under Irix, Linux, and Windoze. +#if defined(__OS_IRIX_) + +void newStringByPipe(void *) + +#elif defined(__OS_Linux_) + +void *newStringByPipe(void *) + +#elif defined(__OS_Win_) + +void newStringByPipe(void *) + +#endif + +{ + extern int numStrings, notDone; + extern char **inputString; + int i; + + // Malloc inputString. + inputString = (char **) malloc(MAX_IN_STRINGS * sizeof(char *)); + for ( i=0;i MAX_IN_STRINGS) { + fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); + numStrings--; + } + inOne++; + if (inOne == MAX_IN_STRINGS) inOne = 0; + } + } + + // Free inputString. + for ( i=0;i maxfd) maxfd = accept_id; + FD_CLR(soc_id, &rmask); + } + for (fd=0;fd<=maxfd;fd++) { // look for other sockets with data + if (FD_ISSET(fd, &rmask)) { // process the data + parsing = 1; + while (parsing) { + i = recv(fd, socBuf, STRING_LEN,0); + if (i==0) { + printf("Closing a socket connection.\n"); + FD_CLR(fd, &mask); +#if defined(__OS_Win_) + closesocket(fd); +#else + close(fd); +#endif + parsing = 0; + } + n = 0; + while (n < i) { + inputString[inOne][m++] = socBuf[n]; + if (socBuf[n++] == '\n') { + if (inputString[inOne][2] == 'i' && inputString[inOne][3] == 't' + && inputString[inOne][1] == 'x' && inputString[inOne][0] == 'E') { + notDone = 0; + n = i; + parsing = 0; + } + else { + m = 0; + if (n >= i) parsing = 0; + numStrings++; + if (numStrings > MAX_IN_STRINGS) { + fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); + numStrings--; + } + inOne++; + if (inOne == MAX_IN_STRINGS) inOne = 0; + memset(inputString[inOne], 0, STRING_LEN); + } + } + } + } + } + } + } +#if defined(__OS_Win_) // Stupid Windoze only stuff + closesocket(soc_id); + WSACleanup(); +#else + shutdown(soc_id,0); +#endif + + // Free inputString. + for ( i=0;i - -#define COM_NO_WINDOWS_H -#include - -#define _FACDS 0x878 -#define MAKE_DSHRESULT(code) MAKE_HRESULT(1, _FACDS, code) - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -// DirectSound Component GUID {47D4D946-62E8-11cf-93BC-444553540000} -DEFINE_GUID(CLSID_DirectSound, 0x47d4d946, 0x62e8, 0x11cf, 0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); - -// DirectSound Capture Component GUID {B0210780-89CD-11d0-AF08-00A0C925CD16} -DEFINE_GUID(CLSID_DirectSoundCapture, 0xb0210780, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); - -// -// Structures -// - -#ifdef __cplusplus -// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined -struct IDirectSound; -struct IDirectSoundBuffer; -struct IDirectSound3DListener; -struct IDirectSound3DBuffer; -struct IDirectSoundCapture; -struct IDirectSoundCaptureBuffer; -struct IDirectSoundNotify; -#endif // __cplusplus - -typedef struct IDirectSound *LPDIRECTSOUND; -typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER; -typedef struct IDirectSound3DListener *LPDIRECTSOUND3DLISTENER; -typedef struct IDirectSound3DBuffer *LPDIRECTSOUND3DBUFFER; -typedef struct IDirectSoundCapture *LPDIRECTSOUNDCAPTURE; -typedef struct IDirectSoundCaptureBuffer *LPDIRECTSOUNDCAPTUREBUFFER; -typedef struct IDirectSoundNotify *LPDIRECTSOUNDNOTIFY; - -typedef struct _DSCAPS -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwMinSecondarySampleRate; - DWORD dwMaxSecondarySampleRate; - DWORD dwPrimaryBuffers; - DWORD dwMaxHwMixingAllBuffers; - DWORD dwMaxHwMixingStaticBuffers; - DWORD dwMaxHwMixingStreamingBuffers; - DWORD dwFreeHwMixingAllBuffers; - DWORD dwFreeHwMixingStaticBuffers; - DWORD dwFreeHwMixingStreamingBuffers; - DWORD dwMaxHw3DAllBuffers; - DWORD dwMaxHw3DStaticBuffers; - DWORD dwMaxHw3DStreamingBuffers; - DWORD dwFreeHw3DAllBuffers; - DWORD dwFreeHw3DStaticBuffers; - DWORD dwFreeHw3DStreamingBuffers; - DWORD dwTotalHwMemBytes; - DWORD dwFreeHwMemBytes; - DWORD dwMaxContigFreeHwMemBytes; - DWORD dwUnlockTransferRateHwBuffers; - DWORD dwPlayCpuOverheadSwBuffers; - DWORD dwReserved1; - DWORD dwReserved2; -} DSCAPS, *LPDSCAPS; - -typedef const DSCAPS *LPCDSCAPS; - -typedef struct _DSBCAPS -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwUnlockTransferRate; - DWORD dwPlayCpuOverhead; -} DSBCAPS, *LPDSBCAPS; - -typedef const DSBCAPS *LPCDSBCAPS; - -typedef struct _DSBUFFERDESC -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - LPWAVEFORMATEX lpwfxFormat; -} DSBUFFERDESC, *LPDSBUFFERDESC; - -typedef const DSBUFFERDESC *LPCDSBUFFERDESC; - -typedef struct _DS3DBUFFER -{ - DWORD dwSize; - D3DVECTOR vPosition; - D3DVECTOR vVelocity; - DWORD dwInsideConeAngle; - DWORD dwOutsideConeAngle; - D3DVECTOR vConeOrientation; - LONG lConeOutsideVolume; - D3DVALUE flMinDistance; - D3DVALUE flMaxDistance; - DWORD dwMode; -} DS3DBUFFER, *LPDS3DBUFFER; - -typedef const DS3DBUFFER *LPCDS3DBUFFER; - -typedef struct _DS3DLISTENER -{ - DWORD dwSize; - D3DVECTOR vPosition; - D3DVECTOR vVelocity; - D3DVECTOR vOrientFront; - D3DVECTOR vOrientTop; - D3DVALUE flDistanceFactor; - D3DVALUE flRolloffFactor; - D3DVALUE flDopplerFactor; -} DS3DLISTENER, *LPDS3DLISTENER; - -typedef const DS3DLISTENER *LPCDS3DLISTENER; - -typedef struct _DSCCAPS -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwFormats; - DWORD dwChannels; -} DSCCAPS, *LPDSCCAPS; - -typedef const DSCCAPS *LPCDSCCAPS; - -typedef struct _DSCBUFFERDESC -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - LPWAVEFORMATEX lpwfxFormat; -} DSCBUFFERDESC, *LPDSCBUFFERDESC; - -typedef const DSCBUFFERDESC *LPCDSCBUFFERDESC; - -typedef struct _DSCBCAPS -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; -} DSCBCAPS, *LPDSCBCAPS; - -typedef const DSCBCAPS *LPCDSCBCAPS; - -typedef struct _DSBPOSITIONNOTIFY -{ - DWORD dwOffset; - HANDLE hEventNotify; -} DSBPOSITIONNOTIFY, *LPDSBPOSITIONNOTIFY; - -typedef const DSBPOSITIONNOTIFY *LPCDSBPOSITIONNOTIFY; - -// -// Compatibility typedefs -// - -typedef LPDIRECTSOUND *LPLPDIRECTSOUND; -typedef LPDIRECTSOUNDBUFFER *LPLPDIRECTSOUNDBUFFER; -typedef LPDIRECTSOUND3DLISTENER *LPLPDIRECTSOUND3DLISTENER; -typedef LPDIRECTSOUND3DBUFFER *LPLPDIRECTSOUND3DBUFFER; -typedef LPDIRECTSOUNDCAPTURE *LPLPDIRECTSOUNDCAPTURE; -typedef LPDIRECTSOUNDCAPTUREBUFFER *LPLPDIRECTSOUNDCAPTUREBUFFER; -typedef LPDIRECTSOUNDNOTIFY *LPLPDIRECTSOUNDNOTIFY; -typedef LPVOID *LPLPVOID; -typedef const WAVEFORMATEX *LPCWAVEFORMATEX; - -// -// DirectSound API -// - -typedef BOOL (CALLBACK *LPDSENUMCALLBACKW)(LPGUID, LPCWSTR, LPCWSTR, LPVOID); -typedef BOOL (CALLBACK *LPDSENUMCALLBACKA)(LPGUID, LPCSTR, LPCSTR, LPVOID); - -extern HRESULT WINAPI DirectSoundCreate(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); -extern HRESULT WINAPI DirectSoundEnumerateW(LPDSENUMCALLBACKW, LPVOID); -extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA, LPVOID); - -extern HRESULT WINAPI DirectSoundCaptureCreate(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN); -extern HRESULT WINAPI DirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW, LPVOID); -extern HRESULT WINAPI DirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA, LPVOID); - -#ifdef UNICODE -#define LPDSENUMCALLBACK LPDSENUMCALLBACKW -#define DirectSoundEnumerate DirectSoundEnumerateW -#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateW -#else // UNICODE -#define LPDSENUMCALLBACK LPDSENUMCALLBACKA -#define DirectSoundEnumerate DirectSoundEnumerateA -#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateA -#endif // UNICODE - -// -// IDirectSound -// - -DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); - -#undef INTERFACE -#define INTERFACE IDirectSound - -DECLARE_INTERFACE_(IDirectSound, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IDirectSound methods - STDMETHOD(CreateSoundBuffer) (THIS_ LPCDSBUFFERDESC, LPDIRECTSOUNDBUFFER *, LPUNKNOWN) PURE; - STDMETHOD(GetCaps) (THIS_ LPDSCAPS) PURE; - STDMETHOD(DuplicateSoundBuffer) (THIS_ LPDIRECTSOUNDBUFFER, LPDIRECTSOUNDBUFFER *) PURE; - STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; - STDMETHOD(Compact) (THIS) PURE; - STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE; - STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE; - STDMETHOD(Initialize) (THIS_ LPGUID) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSound_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirectSound_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirectSound_Release(p) (p)->lpVtbl->Release(p) -#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->lpVtbl->CreateSoundBuffer(p,a,b,c) -#define IDirectSound_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) -#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->lpVtbl->DuplicateSoundBuffer(p,a,b) -#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) -#define IDirectSound_Compact(p) (p)->lpVtbl->Compact(p) -#define IDirectSound_GetSpeakerConfig(p,a) (p)->lpVtbl->GetSpeakerConfig(p,a) -#define IDirectSound_SetSpeakerConfig(p,b) (p)->lpVtbl->SetSpeakerConfig(p,b) -#define IDirectSound_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSound_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirectSound_AddRef(p) (p)->AddRef() -#define IDirectSound_Release(p) (p)->Release() -#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->CreateSoundBuffer(a,b,c) -#define IDirectSound_GetCaps(p,a) (p)->GetCaps(a) -#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->DuplicateSoundBuffer(a,b) -#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) -#define IDirectSound_Compact(p) (p)->Compact() -#define IDirectSound_GetSpeakerConfig(p,a) (p)->GetSpeakerConfig(a) -#define IDirectSound_SetSpeakerConfig(p,b) (p)->SetSpeakerConfig(b) -#define IDirectSound_Initialize(p,a) (p)->Initialize(a) -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -// -// IDirectSoundBuffer -// - -DEFINE_GUID(IID_IDirectSoundBuffer, 0x279AFA85, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); - -#undef INTERFACE -#define INTERFACE IDirectSoundBuffer - -DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IDirectSoundBuffer methods - STDMETHOD(GetCaps) (THIS_ LPDSBCAPS) PURE; - STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; - STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; - STDMETHOD(GetVolume) (THIS_ LPLONG) PURE; - STDMETHOD(GetPan) (THIS_ LPLONG) PURE; - STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE; - STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; - STDMETHOD(Initialize) (THIS_ LPDIRECTSOUND, LPCDSBUFFERDESC) PURE; - STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD) PURE; - STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE; - STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE; - STDMETHOD(SetFormat) (THIS_ LPCWAVEFORMATEX) PURE; - STDMETHOD(SetVolume) (THIS_ LONG) PURE; - STDMETHOD(SetPan) (THIS_ LONG) PURE; - STDMETHOD(SetFrequency) (THIS_ DWORD) PURE; - STDMETHOD(Stop) (THIS) PURE; - STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; - STDMETHOD(Restore) (THIS) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirectSoundBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirectSoundBuffer_Release(p) (p)->lpVtbl->Release(p) -#define IDirectSoundBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) -#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) -#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c) -#define IDirectSoundBuffer_GetVolume(p,a) (p)->lpVtbl->GetVolume(p,a) -#define IDirectSoundBuffer_GetPan(p,a) (p)->lpVtbl->GetPan(p,a) -#define IDirectSoundBuffer_GetFrequency(p,a) (p)->lpVtbl->GetFrequency(p,a) -#define IDirectSoundBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) -#define IDirectSoundBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) -#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) -#define IDirectSoundBuffer_Play(p,a,b,c) (p)->lpVtbl->Play(p,a,b,c) -#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->lpVtbl->SetCurrentPosition(p,a) -#define IDirectSoundBuffer_SetFormat(p,a) (p)->lpVtbl->SetFormat(p,a) -#define IDirectSoundBuffer_SetVolume(p,a) (p)->lpVtbl->SetVolume(p,a) -#define IDirectSoundBuffer_SetPan(p,a) (p)->lpVtbl->SetPan(p,a) -#define IDirectSoundBuffer_SetFrequency(p,a) (p)->lpVtbl->SetFrequency(p,a) -#define IDirectSoundBuffer_Stop(p) (p)->lpVtbl->Stop(p) -#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) -#define IDirectSoundBuffer_Restore(p) (p)->lpVtbl->Restore(p) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirectSoundBuffer_AddRef(p) (p)->AddRef() -#define IDirectSoundBuffer_Release(p) (p)->Release() -#define IDirectSoundBuffer_GetCaps(p,a) (p)->GetCaps(a) -#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b) -#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c) -#define IDirectSoundBuffer_GetVolume(p,a) (p)->GetVolume(a) -#define IDirectSoundBuffer_GetPan(p,a) (p)->GetPan(a) -#define IDirectSoundBuffer_GetFrequency(p,a) (p)->GetFrequency(a) -#define IDirectSoundBuffer_GetStatus(p,a) (p)->GetStatus(a) -#define IDirectSoundBuffer_Initialize(p,a,b) (p)->Initialize(a,b) -#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g) -#define IDirectSoundBuffer_Play(p,a,b,c) (p)->Play(a,b,c) -#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->SetCurrentPosition(a) -#define IDirectSoundBuffer_SetFormat(p,a) (p)->SetFormat(a) -#define IDirectSoundBuffer_SetVolume(p,a) (p)->SetVolume(a) -#define IDirectSoundBuffer_SetPan(p,a) (p)->SetPan(a) -#define IDirectSoundBuffer_SetFrequency(p,a) (p)->SetFrequency(a) -#define IDirectSoundBuffer_Stop(p) (p)->Stop() -#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d) -#define IDirectSoundBuffer_Restore(p) (p)->Restore() -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -// -// IDirectSound3DListener -// - -DEFINE_GUID(IID_IDirectSound3DListener, 0x279AFA84, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); - -#undef INTERFACE -#define INTERFACE IDirectSound3DListener - -DECLARE_INTERFACE_(IDirectSound3DListener, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IDirectSound3D methods - STDMETHOD(GetAllParameters) (THIS_ LPDS3DLISTENER) PURE; - STDMETHOD(GetDistanceFactor) (THIS_ LPD3DVALUE) PURE; - STDMETHOD(GetDopplerFactor) (THIS_ LPD3DVALUE) PURE; - STDMETHOD(GetOrientation) (THIS_ LPD3DVECTOR, LPD3DVECTOR) PURE; - STDMETHOD(GetPosition) (THIS_ LPD3DVECTOR) PURE; - STDMETHOD(GetRolloffFactor) (THIS_ LPD3DVALUE) PURE; - STDMETHOD(GetVelocity) (THIS_ LPD3DVECTOR) PURE; - STDMETHOD(SetAllParameters) (THIS_ LPCDS3DLISTENER, DWORD) PURE; - STDMETHOD(SetDistanceFactor) (THIS_ D3DVALUE, DWORD) PURE; - STDMETHOD(SetDopplerFactor) (THIS_ D3DVALUE, DWORD) PURE; - STDMETHOD(SetOrientation) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; - STDMETHOD(SetPosition) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; - STDMETHOD(SetRolloffFactor) (THIS_ D3DVALUE, DWORD) PURE; - STDMETHOD(SetVelocity) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; - STDMETHOD(CommitDeferredSettings) (THIS) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSound3DListener_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirectSound3DListener_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirectSound3DListener_Release(p) (p)->lpVtbl->Release(p) -#define IDirectSound3DListener_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a) -#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->lpVtbl->GetDistanceFactor(p,a) -#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->lpVtbl->GetDopplerFactor(p,a) -#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->lpVtbl->GetOrientation(p,a,b) -#define IDirectSound3DListener_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a) -#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->lpVtbl->GetRolloffFactor(p,a) -#define IDirectSound3DListener_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a) -#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b) -#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->lpVtbl->SetDistanceFactor(p,a,b) -#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->lpVtbl->SetDopplerFactor(p,a,b) -#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->lpVtbl->SetOrientation(p,a,b,c,d,e,f,g) -#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d) -#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->lpVtbl->SetRolloffFactor(p,a,b) -#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d) -#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->lpVtbl->CommitDeferredSettings(p) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSound3DListener_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirectSound3DListener_AddRef(p) (p)->AddRef() -#define IDirectSound3DListener_Release(p) (p)->Release() -#define IDirectSound3DListener_GetAllParameters(p,a) (p)->GetAllParameters(a) -#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->GetDistanceFactor(a) -#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->GetDopplerFactor(a) -#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->GetOrientation(a,b) -#define IDirectSound3DListener_GetPosition(p,a) (p)->GetPosition(a) -#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->GetRolloffFactor(a) -#define IDirectSound3DListener_GetVelocity(p,a) (p)->GetVelocity(a) -#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b) -#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->SetDistanceFactor(a,b) -#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->SetDopplerFactor(a,b) -#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->SetOrientation(a,b,c,d,e,f,g) -#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d) -#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->SetRolloffFactor(a,b) -#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d) -#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->CommitDeferredSettings() -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -// -// IDirectSound3DBuffer -// - -DEFINE_GUID(IID_IDirectSound3DBuffer, 0x279AFA86, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); - -#undef INTERFACE -#define INTERFACE IDirectSound3DBuffer - -DECLARE_INTERFACE_(IDirectSound3DBuffer, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IDirectSoundBuffer3D methods - STDMETHOD(GetAllParameters) (THIS_ LPDS3DBUFFER) PURE; - STDMETHOD(GetConeAngles) (THIS_ LPDWORD, LPDWORD) PURE; - STDMETHOD(GetConeOrientation) (THIS_ LPD3DVECTOR) PURE; - STDMETHOD(GetConeOutsideVolume) (THIS_ LPLONG) PURE; - STDMETHOD(GetMaxDistance) (THIS_ LPD3DVALUE) PURE; - STDMETHOD(GetMinDistance) (THIS_ LPD3DVALUE) PURE; - STDMETHOD(GetMode) (THIS_ LPDWORD) PURE; - STDMETHOD(GetPosition) (THIS_ LPD3DVECTOR) PURE; - STDMETHOD(GetVelocity) (THIS_ LPD3DVECTOR) PURE; - STDMETHOD(SetAllParameters) (THIS_ LPCDS3DBUFFER, DWORD) PURE; - STDMETHOD(SetConeAngles) (THIS_ DWORD, DWORD, DWORD) PURE; - STDMETHOD(SetConeOrientation) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; - STDMETHOD(SetConeOutsideVolume) (THIS_ LONG, DWORD) PURE; - STDMETHOD(SetMaxDistance) (THIS_ D3DVALUE, DWORD) PURE; - STDMETHOD(SetMinDistance) (THIS_ D3DVALUE, DWORD) PURE; - STDMETHOD(SetMode) (THIS_ DWORD, DWORD) PURE; - STDMETHOD(SetPosition) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; - STDMETHOD(SetVelocity) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSound3DBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirectSound3DBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirectSound3DBuffer_Release(p) (p)->lpVtbl->Release(p) -#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a) -#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->lpVtbl->GetConeAngles(p,a,b) -#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->lpVtbl->GetConeOrientation(p,a) -#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->lpVtbl->GetConeOutsideVolume(p,a) -#define IDirectSound3DBuffer_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a) -#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->lpVtbl->GetMinDistance(p,a) -#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->lpVtbl->GetMaxDistance(p,a) -#define IDirectSound3DBuffer_GetMode(p,a) (p)->lpVtbl->GetMode(p,a) -#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a) -#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b) -#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->lpVtbl->SetConeAngles(p,a,b,c) -#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->lpVtbl->SetConeOrientation(p,a,b,c,d) -#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b)(p)->lpVtbl->SetConeOutsideVolume(p,a,b) -#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d) -#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->lpVtbl->SetMinDistance(p,a,b) -#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->lpVtbl->SetMaxDistance(p,a,b) -#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->lpVtbl->SetMode(p,a,b) -#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSound3DBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirectSound3DBuffer_AddRef(p) (p)->AddRef() -#define IDirectSound3DBuffer_Release(p) (p)->Release() -#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->GetAllParameters(a) -#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->GetConeAngles(a,b) -#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->GetConeOrientation(a) -#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->GetConeOutsideVolume(a) -#define IDirectSound3DBuffer_GetPosition(p,a) (p)->GetPosition(a) -#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->GetMinDistance(a) -#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->GetMaxDistance(a) -#define IDirectSound3DBuffer_GetMode(p,a) (p)->GetMode(a) -#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->GetVelocity(a) -#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b) -#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->SetConeAngles(a,b,c) -#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->SetConeOrientation(a,b,c,d) -#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b)(p)->SetConeOutsideVolume(a,b) -#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d) -#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->SetMinDistance(a,b) -#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->SetMaxDistance(a,b) -#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->SetMode(a,b) -#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d) -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -// -// IDirectSoundCapture -// - -DEFINE_GUID(IID_IDirectSoundCapture, 0xb0210781, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); - -#undef INTERFACE -#define INTERFACE IDirectSoundCapture - -DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IDirectSoundCapture methods - STDMETHOD(CreateCaptureBuffer) (THIS_ LPCDSCBUFFERDESC, LPDIRECTSOUNDCAPTUREBUFFER *, LPUNKNOWN) PURE; - STDMETHOD(GetCaps) (THIS_ LPDSCCAPS ) PURE; - STDMETHOD(Initialize) (THIS_ LPGUID) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundCapture_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirectSoundCapture_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirectSoundCapture_Release(p) (p)->lpVtbl->Release(p) -#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->lpVtbl->CreateCaptureBuffer(p,a,b,c) -#define IDirectSoundCapture_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) -#define IDirectSoundCapture_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundCapture_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirectSoundCapture_AddRef(p) (p)->AddRef() -#define IDirectSoundCapture_Release(p) (p)->Release() -#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->CreateCaptureBuffer(a,b,c) -#define IDirectSoundCapture_GetCaps(p,a) (p)->GetCaps(a) -#define IDirectSoundCapture_Initialize(p,a) (p)->Initialize(a) -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -// -// IDirectSoundCaptureBuffer -// - -DEFINE_GUID(IID_IDirectSoundCaptureBuffer, 0xb0210782, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); - -#undef INTERFACE -#define INTERFACE IDirectSoundCaptureBuffer - -DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IDirectSoundCaptureBuffer methods - STDMETHOD(GetCaps) (THIS_ LPDSCBCAPS ) PURE; - STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD ) PURE; - STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD ) PURE; - STDMETHOD(GetStatus) (THIS_ LPDWORD ) PURE; - STDMETHOD(Initialize) (THIS_ LPDIRECTSOUNDCAPTURE, LPCDSCBUFFERDESC) PURE; - STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD) PURE; - STDMETHOD(Start) (THIS_ DWORD) PURE; - STDMETHOD(Stop) (THIS) PURE; - STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundCaptureBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirectSoundCaptureBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirectSoundCaptureBuffer_Release(p) (p)->lpVtbl->Release(p) -#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) -#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) -#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c) -#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) -#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) -#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) -#define IDirectSoundCaptureBuffer_Start(p,a) (p)->lpVtbl->Start(p,a) -#define IDirectSoundCaptureBuffer_Stop(p) (p)->lpVtbl->Stop(p) -#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundCaptureBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirectSoundCaptureBuffer_AddRef(p) (p)->AddRef() -#define IDirectSoundCaptureBuffer_Release(p) (p)->Release() -#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->GetCaps(a) -#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b) -#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c) -#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->GetStatus(a) -#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->Initialize(a,b) -#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g) -#define IDirectSoundCaptureBuffer_Start(p,a) (p)->Start(a) -#define IDirectSoundCaptureBuffer_Stop(p) (p)->Stop() -#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d) -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -// -// IDirectSoundNotify -// - -DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); - -#undef INTERFACE -#define INTERFACE IDirectSoundNotify - -DECLARE_INTERFACE_(IDirectSoundNotify, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IDirectSoundNotify methods - STDMETHOD(SetNotificationPositions) (THIS_ DWORD, LPCDSBPOSITIONNOTIFY) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundNotify_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirectSoundNotify_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirectSoundNotify_Release(p) (p)->lpVtbl->Release(p) -#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->lpVtbl->SetNotificationPositions(p,a,b) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IDirectSoundNotify_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirectSoundNotify_AddRef(p) (p)->AddRef() -#define IDirectSoundNotify_Release(p) (p)->Release() -#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->SetNotificationPositions(a,b) -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -// -// IKsPropertySet -// - -#ifndef _IKsPropertySet_ -#define _IKsPropertySet_ - -#ifdef __cplusplus -// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined -struct IKsPropertySet; -#endif // __cplusplus - -typedef struct IKsPropertySet *LPKSPROPERTYSET; - -#define KSPROPERTY_SUPPORT_GET 0x00000001 -#define KSPROPERTY_SUPPORT_SET 0x00000002 - -DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); - -#undef INTERFACE -#define INTERFACE IKsPropertySet - -DECLARE_INTERFACE_(IKsPropertySet, IUnknown) -{ - // IUnknown methods - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - - // IKsPropertySet methods - STDMETHOD(Get) (THIS_ REFGUID, ULONG, LPVOID, ULONG, LPVOID, ULONG, PULONG) PURE; - STDMETHOD(Set) (THIS_ REFGUID, ULONG, LPVOID, ULONG, LPVOID, ULONG) PURE; - STDMETHOD(QuerySupport) (THIS_ REFGUID, ULONG, PULONG) PURE; -}; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IKsPropertySet_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IKsPropertySet_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IKsPropertySet_Release(p) (p)->lpVtbl->Release(p) -#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->lpVtbl->Get(p,a,b,c,d,e,f,g) -#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->lpVtbl->Set(p,a,b,c,d,e,f) -#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->lpVtbl->QuerySupport(p,a,b,c) -#else // !defined(__cplusplus) || defined(CINTERFACE) -#define IKsPropertySet_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IKsPropertySet_AddRef(p) (p)->AddRef() -#define IKsPropertySet_Release(p) (p)->Release() -#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->Get(a,b,c,d,e,f,g) -#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->Set(a,b,c,d,e,f) -#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->QuerySupport(a,b,c) -#endif // !defined(__cplusplus) || defined(CINTERFACE) - -#endif // _IKsPropertySet_ - -// -// Return Codes -// - -#define DS_OK 0 - -// The call failed because resources (such as a priority level) -// were already being used by another caller. -#define DSERR_ALLOCATED MAKE_DSHRESULT(10) - -// The control (vol,pan,etc.) requested by the caller is not available. -#define DSERR_CONTROLUNAVAIL MAKE_DSHRESULT(30) - -// An invalid parameter was passed to the returning function -#define DSERR_INVALIDPARAM E_INVALIDARG - -// This call is not valid for the current state of this object -#define DSERR_INVALIDCALL MAKE_DSHRESULT(50) - -// An undetermined error occured inside the DirectSound subsystem -#define DSERR_GENERIC E_FAIL - -// The caller does not have the priority level required for the function to -// succeed. -#define DSERR_PRIOLEVELNEEDED MAKE_DSHRESULT(70) - -// Not enough free memory is available to complete the operation -#define DSERR_OUTOFMEMORY E_OUTOFMEMORY - -// The specified WAVE format is not supported -#define DSERR_BADFORMAT MAKE_DSHRESULT(100) - -// The function called is not supported at this time -#define DSERR_UNSUPPORTED E_NOTIMPL - -// No sound driver is available for use -#define DSERR_NODRIVER MAKE_DSHRESULT(120) - -// This object is already initialized -#define DSERR_ALREADYINITIALIZED MAKE_DSHRESULT(130) - -// This object does not support aggregation -#define DSERR_NOAGGREGATION CLASS_E_NOAGGREGATION - -// The buffer memory has been lost, and must be restored. -#define DSERR_BUFFERLOST MAKE_DSHRESULT(150) - -// Another app has a higher priority level, preventing this call from -// succeeding. -#define DSERR_OTHERAPPHASPRIO MAKE_DSHRESULT(160) - -// This object has not been initialized -#define DSERR_UNINITIALIZED MAKE_DSHRESULT(170) - -// The requested COM interface is not available -#define DSERR_NOINTERFACE E_NOINTERFACE - -// -// Flags -// - -#define DSCAPS_PRIMARYMONO 0x00000001 -#define DSCAPS_PRIMARYSTEREO 0x00000002 -#define DSCAPS_PRIMARY8BIT 0x00000004 -#define DSCAPS_PRIMARY16BIT 0x00000008 -#define DSCAPS_CONTINUOUSRATE 0x00000010 -#define DSCAPS_EMULDRIVER 0x00000020 -#define DSCAPS_CERTIFIED 0x00000040 -#define DSCAPS_SECONDARYMONO 0x00000100 -#define DSCAPS_SECONDARYSTEREO 0x00000200 -#define DSCAPS_SECONDARY8BIT 0x00000400 -#define DSCAPS_SECONDARY16BIT 0x00000800 - -#define DSBPLAY_LOOPING 0x00000001 - -#define DSBSTATUS_PLAYING 0x00000001 -#define DSBSTATUS_BUFFERLOST 0x00000002 -#define DSBSTATUS_LOOPING 0x00000004 - -#define DSBLOCK_FROMWRITECURSOR 0x00000001 -#define DSBLOCK_ENTIREBUFFER 0x00000002 - -#define DSSCL_NORMAL 0x00000001 -#define DSSCL_PRIORITY 0x00000002 -#define DSSCL_EXCLUSIVE 0x00000003 -#define DSSCL_WRITEPRIMARY 0x00000004 - -#define DS3DMODE_NORMAL 0x00000000 -#define DS3DMODE_HEADRELATIVE 0x00000001 -#define DS3DMODE_DISABLE 0x00000002 - -#define DSBCAPS_PRIMARYBUFFER 0x00000001 -#define DSBCAPS_STATIC 0x00000002 -#define DSBCAPS_LOCHARDWARE 0x00000004 -#define DSBCAPS_LOCSOFTWARE 0x00000008 -#define DSBCAPS_CTRL3D 0x00000010 -#define DSBCAPS_CTRLFREQUENCY 0x00000020 -#define DSBCAPS_CTRLPAN 0x00000040 -#define DSBCAPS_CTRLVOLUME 0x00000080 -#define DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 -#define DSBCAPS_CTRLDEFAULT 0x000000E0 -#define DSBCAPS_CTRLALL 0x000001F0 -#define DSBCAPS_STICKYFOCUS 0x00004000 -#define DSBCAPS_GLOBALFOCUS 0x00008000 -#define DSBCAPS_GETCURRENTPOSITION2 0x00010000 -#define DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 - -#define DSCBCAPS_WAVEMAPPED 0x80000000 - -#define DSSPEAKER_HEADPHONE 0x00000001 -#define DSSPEAKER_MONO 0x00000002 -#define DSSPEAKER_QUAD 0x00000003 -#define DSSPEAKER_STEREO 0x00000004 -#define DSSPEAKER_SURROUND 0x00000005 - -#define DSSPEAKER_GEOMETRY_MIN 0x00000005 // 5 degrees -#define DSSPEAKER_GEOMETRY_NARROW 0x0000000A // 10 degrees -#define DSSPEAKER_GEOMETRY_WIDE 0x00000014 // 20 degrees -#define DSSPEAKER_GEOMETRY_MAX 0x000000B4 // 180 degrees - -#define DSSPEAKER_COMBINED(c, g) ((DWORD)(((BYTE)(c)) | ((DWORD)((BYTE)(g))) << 16)) -#define DSSPEAKER_CONFIG(a) ((BYTE)(a)) -#define DSSPEAKER_GEOMETRY(a) ((BYTE)(((DWORD)(a) >> 16) & 0x00FF)) - -#define DSBFREQUENCY_MIN 100 -#define DSBFREQUENCY_MAX 100000 -#define DSBFREQUENCY_ORIGINAL 0 - -#define DSBPAN_LEFT -10000 -#define DSBPAN_CENTER 0 -#define DSBPAN_RIGHT 10000 - -#define DSBVOLUME_MIN -10000 -#define DSBVOLUME_MAX 0 - -#define DSBSIZE_MIN 4 -#define DSBSIZE_MAX 0x0FFFFFFF - -#define DS3D_IMMEDIATE 0x00000000 -#define DS3D_DEFERRED 0x00000001 - -#define DS3D_MINDISTANCEFACTOR FLT_MIN -#define DS3D_MAXDISTANCEFACTOR FLT_MAX -#define DS3D_DEFAULTDISTANCEFACTOR 1.0f - -#define DS3D_MINROLLOFFFACTOR 0.0f -#define DS3D_MAXROLLOFFFACTOR 10.0f -#define DS3D_DEFAULTROLLOFFFACTOR 1.0f - -#define DS3D_MINDOPPLERFACTOR 0.0f -#define DS3D_MAXDOPPLERFACTOR 10.0f -#define DS3D_DEFAULTDOPPLERFACTOR 1.0f - -#define DS3D_DEFAULTMINDISTANCE 1.0f -#define DS3D_DEFAULTMAXDISTANCE 1000000000.0f - -#define DS3D_MINCONEANGLE 0 -#define DS3D_MAXCONEANGLE 360 -#define DS3D_DEFAULTCONEANGLE 360 - -#define DS3D_DEFAULTCONEOUTSIDEVOLUME DSBVOLUME_MAX - -#define DSCCAPS_EMULDRIVER 0x00000020 - -#define DSCBLOCK_ENTIREBUFFER 0x00000001 - -#define DSCBSTATUS_CAPTURING 0x00000001 -#define DSCBSTATUS_LOOPING 0x00000002 - -#define DSCBSTART_LOOPING 0x00000001 - -#define DSBPN_OFFSETSTOP 0xFFFFFFFF - -#ifdef __cplusplus -}; -#endif // __cplusplus - -#endif // __DSOUND_INCLUDED__ diff --git a/mus151/GUITwoOsc b/mus151/GUITwoOsc index 8d76b11..516b651 100755 --- a/mus151/GUITwoOsc +++ b/mus151/GUITwoOsc @@ -1 +1 @@ -wish < tcl/TwoWaves.tcl | MUS151 TwoOsc -r \ No newline at end of file +wish < tcl/TwoWaves.tcl | MUS151 TwoOsc -ip \ No newline at end of file diff --git a/mus151/MUS151.cpp b/mus151/MUS151.cpp index 5662fa9..2068bf2 100644 --- a/mus151/MUS151.cpp +++ b/mus151/MUS151.cpp @@ -2,261 +2,68 @@ /******** Center for Computer Research in Music & Acoustics ******/ /******** Stanford University, by Gary P. Scavone, 1998 **********/ -#include "../miditabl.h" -#include "../WvOut.h" -#include "../RTWvOut.h" -#include "../SKINI11.h" +#include "../STK/WvOut.h" +#include "../STK/RTWvOut.h" +#include "../STK/SKINI11.h" +#include "../STK/SKINI11.msg" #include "TwoOsc.h" - -#include "../SKINI11.msg" - -#define INSTR_LEN 60 +#include "miditabl.h" int numStrings = 0; int notDone = 1; +char **inputString; -#if defined(__SGI_REALTIME_) - -#include -#include -#include - -char inputString[MAX_IN_STRINGS][INSTR_LEN]; -pid_t string_thread; - -void newString(void *) -{ - int inOne = 0; - while (notDone) { - fgets(inputString[inOne],INSTR_LEN,stdin); - if (inputString[inOne][0] == 'E' && inputString[inOne][1] == 'x') { - notDone = 0; - } - else { - numStrings++; - if (numStrings > MAX_IN_STRINGS) { - fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); - numStrings--; - } - inOne++; - if (inOne == MAX_IN_STRINGS) inOne = 0; - } - } -} - -#elif defined(__USS_REALTIME_) - -#include -//#include - -char inputString[MAX_IN_STRINGS][INSTR_LEN]; -pthread_t string_thread; - -void *newString(void *) -{ - int inOne = 0; - while (notDone) { - fgets(inputString[inOne],INSTR_LEN,stdin); - if (inputString[inOne][0] == 'E' && inputString[inOne][1] == 'x') { - notDone = 0; - } - else { - numStrings++; - if (numStrings > MAX_IN_STRINGS) { - fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); - numStrings--; - } - inOne++; - if (inOne == MAX_IN_STRINGS) inOne = 0; - } - } -} - -#elif (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) - -#include -#include - -char inputString[MAX_IN_STRINGS][INSTR_LEN]; -unsigned long string_thread; - -#define SERVICE_PORT 2001 - -void newString(void *) -{ - int inOne = 0, i=0, m=0, n; - SOCKET soc_id, accept_id; - WSADATA wsaData; - int nRet; - struct sockaddr_in sockname; - WORD wVersionRequested = MAKEWORD(1,1); - char socBuf[INSTR_LEN]; - - nRet = WSAStartup(wVersionRequested, &wsaData); - if (wsaData.wVersion != wVersionRequested) - { - fprintf(stderr,"\n Wrong version\n"); - exit(0); - } - - /* Create the server-side socket */ - soc_id = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); - if(soc_id == INVALID_SOCKET) { - fprintf(stderr,"Couldn't create socket!\n"); - exit(0); - } - - sockname.sin_family=AF_INET; - sockname.sin_addr.s_addr=INADDR_ANY; - sockname.sin_port=htons(SERVICE_PORT); - - /* Bind socket to the appropriate port and interface (INADDR_ANY) */ - if (bind(soc_id,(LPSOCKADDR)&sockname,sizeof(sockname))==SOCKET_ERROR) { - fprintf(stderr,"Couldn't bind socket!\n"); - exit(0); - } - - /* Listen for incoming connections */ - printf("Listening for socket on port %d\n", SERVICE_PORT); - if (listen(soc_id,1)==SOCKET_ERROR) { - fprintf(stderr,"Couldn't set up listen on socket!\n"); - exit(0); - } - - /* Accept and service one incoming connection request */ - accept_id=accept(soc_id,NULL,NULL); - if (accept_id==INVALID_SOCKET) { - fprintf(stderr,"Couldn't accept incoming connection on socket!\n"); - exit(0); - } - - memset(socBuf, 0, sizeof(socBuf)); - - printf("Socket connection made ... ready to receive SKINI messages.\n"); - while (notDone) { - i = recv(accept_id, socBuf, INSTR_LEN, 0); - if (i==0) notDone = 0; - n = 0; - while (n < i) { - inputString[inOne][m++] = socBuf[n]; - if (socBuf[n++] == '\n') { - if (inputString[inOne][0] == 'E' && inputString[inOne][1] == 'x') { - notDone = 0; - n = i; - } - else { - m = 0; - numStrings++; - if (numStrings > MAX_IN_STRINGS) { - fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); - numStrings--; - } - inOne++; - if (inOne == MAX_IN_STRINGS) inOne = 0; - } - memset(inputString[inOne], 0, INSTR_LEN); - } - } - } - closesocket(accept_id); - closesocket(soc_id); - WSACleanup(); - printf("Socket connection closed.\n"); -} - -#else -char inputString[1][INSTR_LEN]; -#endif +// The input command pipe and socket threads are defined in threads.cpp. +#include "threads.h" /* Error function in case of incorrect command-line argument specifications */ void errorf(char *func) { - printf("\nuseage: %s Instr \n",func); - printf(" where Instr = TwoOsc (only one available for now)\n\n"); + printf("\nuseage: %s Instr flag\n",func); + printf(" where Instr = TwoOsc (only one available for now)\n"); + printf(" and flag = -ip for realtime SKINI input by pipe\n"); + printf(" (won't work under Win95/98),\n"); + printf(" or flag = -is for realtime SKINI input by socket.\n"); exit(0); } void main(int argc,char *argv[]) { - long i, j, synlength; + long i, j, synlength, useSocket = 0; int type, rtInput = 0; int outOne = 0; - char *fin; MY_FLOAT settleTime = 0.5; /* in seconds */ MY_FLOAT temp, byte3, lastPitch; - WvOut *output; - TwoOsc *instrument; - SKINI11 *score; - if (argc > 1) { - if (!strcmp(argv[1],"TwoOsc")) instrument = new TwoOsc; - else errorf(argv[0]); + if (argc == 3) { + if (strcmp(argv[1],"TwoOsc")) errorf(argv[0]); + if (!strcmp(argv[2],"-is")) useSocket = 1; + else if (strcmp(argv[2],"-ip")) errorf(argv[0]); } else errorf(argv[0]); - output = new RTWvOut(); - score = new SKINI11(); + TwoOsc *instrument = new TwoOsc; + WvOut *output = new RTWvOut(SRATE,1); + SKINI11 *score = new SKINI11(); - rtInput = 1; /* We're going to always use realtime input */ + // Start the input thread. + if (useSocket) + startSocketThread(); + else + startPipeThread(); + instrument->noteOn(200.0,0.1); - /* If using realtime input, setup the input thread. */ -#if defined(__SGI_REALTIME_) - if (rtInput) { - string_thread = sproc(newString, PR_SALL); - if (string_thread == -1) { - fprintf(stderr, "unable to create input thread...aborting.\n"); - exit(-1); - } - instrument->noteOn(200.0, 0.2); - } -#elif defined(__USS_REALTIME_) - if (rtInput) { - if (pthread_create(&string_thread, NULL, newString, NULL)) - { - fprintf(stderr, "unable to create input thread...aborting.\n"); - exit(-1); - } - instrument->noteOn(200.0,0.2); - } -#elif (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) - if (rtInput) { - string_thread = _beginthread(newString, 0, NULL); - if (string_thread == -1) { - fprintf(stderr, "Unable to create exit thread.\n"); - printf("Exiting MD2SKINI process.\n"); - exit(0); - } - instrument->noteOn(200.0,0.2); - } -#endif - - /* Finally ... the runtime loop begins! */ + // The runtime loop begins here: notDone = 1; synlength = RT_BUFFER_SIZE; while(notDone || numStrings) { - if (rtInput) { - if (numStrings > 1) synlength = (long) RT_BUFFER_SIZE / numStrings; - else synlength = RT_BUFFER_SIZE; - for ( i=0; itick(instrument->tick()); - } - } - else { - fin = fgets(inputString[0],INSTR_LEN,stdin); - if (fin == NULL) notDone = 0; - else { - numStrings++; - } + if (numStrings > 1) synlength = (long) RT_BUFFER_SIZE / numStrings; + else synlength = RT_BUFFER_SIZE; + for ( i=0; itick(instrument->tick()); } if (numStrings) { score->parseThis(inputString[outOne]); type = score->getType(); if (type > 0) { - if (temp = score->getDelta()) { /* SKINI score file */ - synlength = (long) (temp * SRATE); - for ( i=0; itick(instrument->tick()); - } - synlength = 0; - } if (type == __SK_NoteOn_ ) { if (( byte3 = score->getByteThree() ) == 0) instrument->noteOff(byte3*NORM_7); @@ -290,10 +97,8 @@ void main(int argc,char *argv[]) else if (type == __SK_ProgramChange_) { } } - if (rtInput) { - outOne += 1; - if (outOne == MAX_IN_STRINGS) outOne = 0; - } + outOne += 1; + if (outOne == MAX_IN_STRINGS) outOne = 0; numStrings--; } } @@ -305,8 +110,5 @@ void main(int argc,char *argv[]) delete score; delete instrument; -#if defined(__SGI_REALTIME_) - if (rtInput) kill(string_thread, SIGKILL); -#endif printf("MUS151 finished.\n"); } diff --git a/mus151/MUS151.dsp b/mus151/MUS151.dsp index 2bf5980..3c7b0ea 100644 --- a/mus151/MUS151.dsp +++ b/mus151/MUS151.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__OS_Win_" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -65,7 +65,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__OS_Win_" /YX /FD /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -83,11 +83,11 @@ LINK32=link.exe # Name "MUS151 - Win32 Debug" # Begin Source File -SOURCE=..\Envelope.cpp +SOURCE=..\Stk\Envelope.cpp # End Source File # Begin Source File -SOURCE=..\Envelope.h +SOURCE=..\Stk\Envelope.h # End Source File # Begin Source File @@ -95,51 +95,43 @@ SOURCE=.\MUS151.cpp # End Source File # Begin Source File -SOURCE=..\Object.cpp +SOURCE=..\Stk\Object.cpp # End Source File # Begin Source File -SOURCE=..\Object.h +SOURCE=..\Stk\Object.h # End Source File # Begin Source File -SOURCE=..\RawLoop.cpp +SOURCE=..\Stk\RawWvIn.cpp # End Source File # Begin Source File -SOURCE=..\RawLoop.h +SOURCE=..\Stk\RawWvIn.h # End Source File # Begin Source File -SOURCE=..\RawWave.cpp +SOURCE=..\Stk\RTSoundIO.cpp # End Source File # Begin Source File -SOURCE=..\RawWave.h +SOURCE=..\Stk\RTSoundIO.h # End Source File # Begin Source File -SOURCE=..\RTSoundIO.cpp +SOURCE=..\Stk\RTWvOut.cpp # End Source File # Begin Source File -SOURCE=..\RTSoundIO.h +SOURCE=..\Stk\RTWvOut.h # End Source File # Begin Source File -SOURCE=..\RTWvOut.cpp +SOURCE=..\Stk\SKINI11.cpp # End Source File # Begin Source File -SOURCE=..\RTWvOut.h -# End Source File -# Begin Source File - -SOURCE=..\SKINI11.cpp -# End Source File -# Begin Source File - -SOURCE=..\SKINI11.h +SOURCE=..\Stk\SKINI11.h # End Source File # Begin Source File @@ -151,11 +143,19 @@ SOURCE=..\SKINI11.tbl # End Source File # Begin Source File -SOURCE=..\swapstuf.cpp +SOURCE=..\Stk\swapstuf.cpp # End Source File # Begin Source File -SOURCE=..\swapstuf.h +SOURCE=..\Stk\swapstuf.h +# End Source File +# Begin Source File + +SOURCE=.\threads.cpp +# End Source File +# Begin Source File + +SOURCE=.\threads.h # End Source File # Begin Source File @@ -167,11 +167,19 @@ SOURCE=.\TwoOsc.h # End Source File # Begin Source File -SOURCE=..\WvOut.cpp +SOURCE=..\Stk\WvIn.cpp # End Source File # Begin Source File -SOURCE=..\WvOut.h +SOURCE=..\Stk\WvIn.h +# End Source File +# Begin Source File + +SOURCE=..\Stk\WvOut.cpp +# End Source File +# Begin Source File + +SOURCE=..\Stk\WvOut.h # End Source File # End Target # End Project diff --git a/mus151/MUS151.ncb b/mus151/MUS151.ncb index dfe09388d4c06b22f748122efe7c8175f1d3b544..f753034bf4c03d60c178af10156978a893362fdf 100644 GIT binary patch delta 12298 zcmc&)32;?an*QH=Nywg;?PY`HCO`-g5&{?@AiM`jI*~0TK|mw$NM1-Fn@Iox@dn2R z+67H6E_7>)j*N)7#;sZ$t#WOpyLt+%hsC`dOM6;HyGQKC!TG*>&wY7GaB8M%s^+}9 z??2yJ|MQ>!{AcZZ%hmT*#2wRPTIHBiB=+o1XX9XQ)$6X9SdkxRBS_@zU}D(UIffP% z7D*`b%75A+QBRjghB+LAQ76xhd9Y^hf_ZL{BoQq_BFvP_6&@pFOx&b!v_zYDDsh=; z;Sz4*47ahMe!N4B=K5m%-&H?9@pI80mPZlmk^^8TOpMR!a~uvMV#TtONYN4`4RLN+ zYsQHdFA2bj@(!^>v~Nr4-w;vmE)t@L_9k|ZNwjBHgIvLp*QO0H5k6YY$X zSh~&p!3m#he0Xg7eKFv>b(WJ7x_+tzKihBLC*qz584MD`Rz{UQQ&mYS-=IL44(x$ z1>seoZq&5u+nu`e1w^zU;0Ms_K>vUQdqIzb-UC{L@WY_&tQSC8(OaO;pn#L0RS5rF z(eFXm0Z0DDDlZ+h2)IDeS)gdARDsSyA0*45^WzK7fmPx9527VPs%-y@PZQ~Cp=Y(I!QyqRT~DtY6Q(25a`(HS@ol2H!8$U@e&D2lXi?~I;zPjh(_S<* z8Q3AeLDke~mK=^j`wHXByiV54Vg596z1NunWebOfI|5KJ1Uf4s#90R2DhZQw0$s>L zFT9yFc>1P%SAWgofrjuD2rOCh$+z_2!$5aO85s?86UB?+n2iXRY=H3HupbRMiCd%~ z9a`^3C3~&VyBgLy28{0-E{W@OpwVt|u7o^$jiPI^QmNji8dIIA+EDS;gKMsN(Al1l zZ1om%;use zU@&JR{M=JM!=-PB56#n(Aauw}ZnUtQeNJq40!pO*5MPV?iOd<*N^Q8{V`?1Mg#fg^Hc2yYgRy-s%k@lXqG zr^A{DY@9255gQqde_UpQq1Ygx;6j)#d42V|hC_@30i!$C07X?8V1X=u7xaJwoXJ92@(#l^O@PO&dJE z?WkkmR=0jLdVmTzS;`>}rEtKUCWv>khtT5_(H7!A;m_6(;`0jUq3y(LtRB@S$b?}y zS8`2kb(j|_OG^@_%MvUQI8m0#90Y{RQz#&DtrSd?>A+ZDz#1fKQZQADfX7Gw zG~h^iL*Y`n3^+>OSNJlS1spApsq!zE`M@z!%lKtdFhgbn>#?$5Ww=yI5fCRW3eS{F zf#YR1akCUmml?nbat{mqQVQnE<-oQ|d1V29w2hXAcx54e$E z$|PCG>};wC#Nop)87hZv$C#y+vM1bR_+i5WNuY`NFjrK9^u~FBnXjrR3$3S1? zRrZ4JuZZ9{h)2aVRmG_mE%_?2@0hk1@@K`qt#3=)@WHkF{+tf ztkE)oW6_tizg#q-UsiB&g~<;US$)esjv6Kkip4huv(FAxq~!9)o4#@m>^+2K@;~k6 zV)>7Ii@~Ed!@(tAvV4G)m~v^`=hn4IxaHQI#H*!X9BgM6>c1P4 zR3Zww)hq;Y(78+S%Yu@-Frm4>G$L-xLB4$aM;ev*-)J=7#?*J+mNWZHF9yI2q#mcL z^ul~i0?rmk6E&6#3~C`Y4_axqnk_cIx-BOa0(OE~YsQ5xFM@nwnF!>i1A8V>!guJ9MYouzO1wMzb0r}SB;~MG=FxO6R zrat8xa1E@BY_{qWQMI8@xX20C2Ib)DE?66s2j&^QXV!)&E~f0-Sj$h>rkj(tW@#nz zShJ$v(putDk2O}}Iik>1L*eC85A2c$6~0nd1Bc7c6<#dMfU#6av+{RJH*loPQusD` z2RKR>w+iT$tq6#gYK4C%CxK&RrNXD>ec)J`qVOKM1vpMts|u&WgmJpC@g&Sgta^!x z&&DJ{9owpKg}DyWljV68aJ8&Kf>ilX;dRmuoF=Ub&x6h6tedMC*d{**&J>@DPr@+7 zxG;wdN5>!*Q=#-lbc_!IIfqpIUd7iv75&b2G7~OU8R+M?OX1_FfcUIuFthbaCuc^r z6w?wg(Qxq*YI}&SUh`r&aDdsp=7k6*0^7aj#hjlHZ1*87$cZf(YMhN; zdy81vrfl}w!whH<(-vh@w}H81n~YyJdd=!)uXCzQ1&)>HQ~@+*+_YM~?nNIJ0>@ju z-s{CYS_GUR=T-VF2$gHcFW%%(*`sOnt%fU~Mx^qmeEj5U-Yk4D@g|dfieZFIH7KlC2}q(j%{o$1fXP zT54q39j*v1HS%KBQ~;zYHS%I9k_Vip)X1`O*(l?LY7ln|+2{sKt*ku-br%YNQ4Q;a z?$NEOmWGVeFw8fyIME8r&~f8{y$m>YVlEjnd}HCq^h+~e$-Og8eI~vvY%(|vaGN&{ zA59_6Keu^F7t`lCran@k&w;CYrPQIy&2TC>mf!ku1o(o&+t`O}S@dG<$tBN0w356EV+T3xUkqaCqrA$9rv__%b4p-Ye zu3D!7kCC@k{yA9W!Q#my7M`h1m)Qu2k*`$(pHu?JNfHZOEn1aa4ID3ZjK^j-L$U^6 zytmplISr$h=Is*bHkY2osHp@?QZ5_0QsnY-Hdb(RaENxoHE=NOf0^UK!(ooOgN~y+ zVx8|K^!to($nH~mCc|?cQsW64Pqf6G%1;lu+bk%PIBz6pI=m80L+l7`qJXXhzSQmjFqXbUe(P*DF<@WXa za$Hq?g=l_?wGC%E)0GJJBpDYT-2a%Nr60jRGcojw!^L)UBYCI z{k}QQE1dnlnUng=fT2G- z!#pJBnweG$%h`TkiX*(=mocz7Y)JA|H}3Wt4?I63!#k9pKD&&Ksm52&?2EQ#wL8_= z{p`LpJN6(-=MpT)lIDP3mKaYyn>V2^v>|o|oHq^6kTP&=-cKhVhvb~MPz798zb~6z zQ;iG>6A#x;zENAjMWf9ec2efFsG+^qVupea3>=HkQiMw~ zthPf9;~jm*^2aWvm}V+1$xk;vdwySprCw0Kr;kO3ITxl6M!)i%>*?eF>e9>08aDX5 zw>9+iZfdCL*b->#>sM~kM}*Zas$N=MQWBcX4r=e&kTSd#fuMNhG%wb8RQ6C{`%ET1O zu$I~)7}+m&`0&Dew)(qzdN((Z;wBg|lwN1QtA4EC+PSQ!aa6A1u`R*=3~EbAA5%VC zsyo&vK?QFUpqJ9@`IoJz!=#tfIm#kP^UoF*3>q|=cKV$l^g6oD- zj}jH4>?0=owYwP(w#W+3jU*c{V`f=k{#tDemd@L*M7w zk|--v38b;b->nsD?VU{leJJ(@J33TUY*DaW$r~YDAs)?CQ0d6l+(37CN2ke$9bQ(} z9cb+AZd$Opc|miaTRJ!Qu5E7mZ^~(rxlQeXrdEHke{)l-S=ijB9_l>GHSYhd$F;V# zgH&IOvLvYWWo2z$<*lf!*Bl@tBI-~-s~?`cZlS-ccXM|@7T1(l*H$d7uUocaY5785 zP5p}6icx7*5q8?V(8LQ?R905hhGuEnT3%-?e>*vxo`v-3+dDe~b*X)vt3bMyMPfHZ{?O=pgg3Ky(m$WG8quy#`cSu;IlWv2dF-Wx*v-GQD!uS(L< z+1>7MqlB$=h^h7W+DHei><(P3BK=KG^{wr8Bv4I@zh@*|*4#W)Jbcw1y@Bqo&Ni0H zp7M8CA$1skn|p?;;?*jlVzLWtkLHsT(#4cCXc!s!IyMR1+j76mU445@zb{|sLe6$P zhJPI=q!ns?{0Vk_f5RG#N5DKCvfW{|Oy@Bz-FoD+Uk6M>p0hjzgX228o+qww+?CVa zfgMbUn_>ES(!*=Ak&agxbi5F?T@}`vnI4{=@zRVg3+(XX`e16mFR!c^m7X)ht-m`| zDk9!%9Ky+{cW9v+OnE37x#PK@w%vQKeqYhRI$TF6cOFIYWrXdxf27-kD%;e4n=5HL zZa!_tePyjngbPVtm2kaZigIQ_R(r6k`xGCKJ#2RkJnx|{pH-JG?|@$(CGu7x9i72V z2zOH1Nbshra6W{7ND1C9=tG5%sAcGrD&l1@>x3*`LnYI}SQQxAjjsxIIF+owK^nRP zX{!Ajk>O4&%>4gQ^=uCi?^Eb*MC^C*P(H=&NV47OVeDg84T zc@+V?_26BO?INtkSR&v_QKxNE1|7NYmu0MnJAqL08x z2^i#V&Vw$Pr_$e#u8l?hIjG}vsS)8N{%iQYRARS^u>L12eSQa-e}@+5LiGEQj?YC# zx+jo!J2q{Z5xn-{34mt}Z=!nIq)2cN0Ix`{0G1_FZ?FM&$|~g&21DNPyg0{ zll2JiC+G0fT@MbXq2Ks|hUZ2PAnXH_UxhNZL1|{-<2mpbNQ37V^UZrLGbjE|;fn$j z8Z!4G17E?|?lNFraAzVj9e42gQLvQFldREbhVmVa?RYG;9i`oZ0G_msbkBw4I|sJu zc;ZaQI|ANi9!K*hqKPlXVK16=95K9OO9d}{`;nW5P@S)-j-Vc1?S#5gr7;Zq9JxXr z?}O=hlViJnh-8i8gJ%>YT_ft?ZOKWrg~!p#^8s9OSY@WSQ~=$%}A zpP;%+5O&CIe6_)|hS!ID8^*I`x>G2^iz0aPK*!rx+kJxUyAfZ6R!xTg14xhOExh`o z<6DkUR~ijf*26<&%-HcLfd>e@@!?%C?H@!5{&4IwVgyps$GkRMR^4jflyI#bc5g$wvnjL$mgeyN(kV_e(uV2?LC zJk_G(^;ROjHVevBw{h)>O#M6b=Th*YMu!>6cX75`6w*fCi9Y~6dss=G2Ogo0$E(jO zJ?4QLZ_s!e^?Q`hcNE_sjt|n7;NyipT%GyPzdxgg&nh7_qEep4@>RggXeaM1coNJj z>_x!55#WQoe?)gqL!}xj<0F|@kY+v>A<0uiIOF3H+wp3kTJ^>*HDWp;PLC%+iPBC< z>WMYJ+T$@_gz$ul7a??fI$%3DPT@Qeuc2WZanyJBKviS}1<)Ne7=Q8&m+|eX6c^Uf z#XQwsM@xHR*vG5%9xV_U0#;iT9a#;J9VEAJyTj-Wt zhj9Th43#h>iXfK|M=(A_B*A!y#cgvHDjHK(td=Wq@;N*plu3xlJal2|VGHO5P?a0- zaoASB@Lj{wA3uIIVxC(+w(1{dAxI`bMW=y2fsc0hHRyK^=zCDpw#XH1Y8FC8&otE z)OuDbvJBxSX9y$HgD8jN<35C!fo@0O1kf}L)XPCRE0usw;&O+T{2d=H>*Yu=kA7sx z1-%OuO#!7nK$P5qKQHJrpgzzkh@T8f?iPal8u=!IevLwTSsXyR`Jkl;&ja0pil>8q z3Oot4VkXQ3%$J-&LW=zeFvs&J5FIV#09rB*v<4Lwg04o!@u07O=7YYE0xvaoy_ej6 zBXZJC{SB07d9gRIo51V33qx;S9|Eod{y%v0s`Jt7|J$2atbftN z>Xq+)w1|&sUjv0un{#4PwD*BM3V^z8~MXy)%Rz>es^r)i8NU6HV;dvMI z2T+PM7SD#sz*t2a5dI`^A1dPb2>WrT^Ip&tP(CZ30NMpS6ZCq}a?n*Mp&qmbv`NMH zg7Sp_CQvqXKWKo@h96Q1o>BBQ&|^pti3+)>PY1mY1s8z+06GhFwyeXw=X$*6;Em!{ zxPokuYw$8+EuM)k!v&S<4e0w$z h9M+^#ekN5?jVBk21|7$KbPc+XogMl2RLyS3e*wW6%)kHu delta 402 zcmZvYF-yZx6oqeILQScNQ9%bmhk`y35fQDEi$lhY4uTmS{0TxkW~d-_(4m7kb?9U| zbZ`|ibm-tBI5;SDGlZEbNMJiCp!yD#c#o-p)-5 zZybBh+tS6dleb0Q|HRbozUvfBJ$skeM^+dI;hDYSV3}_*)v5^RNHkr=j9bu?8FoC{ zH$hbMT}$G;-=CoxoYymK}-J>sb8&KUQdE diff --git a/STK98v2.opt b/mus151/MUS151.opt similarity index 61% rename from STK98v2.opt rename to mus151/MUS151.opt index 15fe70acf1ef059969154bc56946f6e1e136f934..e526c7bba81c485984ece9a958a2ff51b10b892a 100644 GIT binary patch literal 48640 zcmeI5U02*h7{_NzL7?vyQR~|nU)oyB^48j7MawQ!DwN8WZc#jYvdOTUl1=s`yQO>! zKZ1JarJukHue`D!q2BiR4Lpkf*-c<+p|CAd;QWX4CzH&Rc_zQf=HfYdCJ+8N@$koA zPW>q!E5o8i{JY#C+8T~0P}i!J;{xr=%eo$KbsdLT_7H)8Fjk3qYk>|zt&pDcm!Nj2 z1L}khL3*B#V0#og2H_SEC!mwi%TO2e3Z(5`#r8Gmb?6lI2J|NM7W6js4y5hIu{EIg zp!cB83~^bw?OKgRYv)B`1o)cK$Fl7XbQRseGN@R8A#jBV0#6(Mou9=3s4!U(rsx^c(E*K%N!(;ohO?)P&?eog4>k4>}i9*6}I zin1_;B@{-I!8lAv#YRurN^ziekLr6@kK-w(D^0a=>YEj11m1nI2>%MT=F4$$AfbOd z`nkAO|A)1k$p5IYg@bF=6rQCVg@65f)xLGvN*~?YDDt1c{OPf`m8xOig+3!SG*|RX zYOY`gOLJjmVXl6BZgM7r=ALxsrnWBXpIWWxy?!}8Ix%`RGumCZlILaC@dj{MxylTc z=KZKuTk6{1wHhj->oN1R%2x}k#@eKTeY@#rLSij{~I!ymPT%JB$bJ(xv5MJ@N(E4D;p8OWJJGg6>)s_4%+;cHf?FI z*-Oi$C5_IGjt(Q9htRJEmXayIRK}$RhFo+iMLF-9g-{wvSN7f??7b|Lwlr>}M@MF^ z8PWM8fxj54K&DL~z*NdfbK!~8J>T^UzD)W?G|qfz#T}%k#>V7?` z*SbfOr`&=59@ojsD!fJ2g6%}LMaK)vzVFs7EWc#Cg{Z}}@0sP;EG#NF?#FjkWP+Mm ztwcN3s$S(qRt)4H>$L*v*~eSUKYmwt z8s#64Ba41qYj4_GLn;T2EJXjDv{3(8=@}PUez@7znTBd{O{(ToU|qZ5+DH*AOR8} z0TLhq64+@1jQ{R5arz^H-9bRde`kNIzxcVqii(}}a9n-e$99oIluQTeHn85{@amF| zyK4Q%#)TN`b;kd={v+3a+#L@mj)DY8fCNZ@1W14cNPq-LVA}~W{iYr8D~O3*&#&!+igb-+!rp?S|ifS$RgVLINZ}0wh2JBtQZrKmsJN zCkQb9wO*VX4O&8|QBy#5|P|9Sj( z;`=}K64N_~-L|Y*v5I|hr9Z9=#FfE#4EkNsemSjNWrm9R-<7R?cW+9YD(v`jU2YWBeq|N+q=6}aOZEi_`1W14cNPq-L zfCNZ@1ojyL#(($Ogz|DoV2cEF{I{)Udl&y1|I2dyN5=mc|J&lzgJvW^0wh2JBtQZr aKmsH{0(*@B<9~Z?#yDOQ*d78p{`ViqO%m4t literal 53760 zcmeHQ-E!N;8HJ=+p_tTFT-SBowB4XTw&RFYNXL~~RgFnX4lPqsM8%9-whjnD6f6+n z5TI<|WLNB99KpdC{-Ukvu61WY>e0Q+@1+W0z z1yaB-fkog~z!D(yeTeNnAPr=IEU*l$0QZ590GUr>74e5a9#{j`feqkeU=t_+GT$fI zJ_3rsr@&+2H^6U!67V}f=6izeXMhHj0Ua;^6W9hSfXsK&VN-GO<0}F!cd)H1FJO82 za0NXktfTBZFAJZ&tNi7I**{Onc7^$huwGYo6;G*w3mxqj;L=CjZ*mdl8?+V5WV64@ zzANi7C|~#SNx36CD?%F9UF8| z+w8=&h9B%S8kW&Ki))niFax4`E!csnYPFfe@Z5&e%3SnZeK*JucU;@8xFYT~%H9jl zH>)Y#bv)P;am&%`X4Q0j%}zPUQZvdawcU8^sUlw0^c~AFQ@P~A9T9gMepe3jxv7e1 zth8y`rskO)-rVxj?!&okF1H>#cyPV^)YN@>a7q5LWgr7&fDDjp|&R#`7IGICPn=Iry`yeYB9H#OkmQ0=vsC;{mK1S{7Ghgt+4)Z zIoQjmGX+FfYHC1fJmfl7(|3^a7-w5?+o>|IabPbi;<`|q^$xEMW`eC(N!F3#F~){6=ZBu#re7Vq5u*pg8U>A7}K$;#hRz=-|H&dab7R zNp4N1n~6&u3h6=falYRL+4So8AhZCOu6f*Gd zFDCx5SY4g;p+PW^0WxsD7?|q(M@K&HQ2(R;cfM`_ww4T#0W#3TK&bu6?O6@f|ET|k z$|Uta@v8o3O#Kg%c2mUeBk+YUA5i~0{(BH-bsS^6MrVNf-{{=2BpEpE3`FquV*cR+ z`D!cmKfeEW+PgFBPX@>U88|ir)c?BPyW{(ReE)Ap^uF%M_y4{gpAqVR)c?K_Kd565 ziaf!YV}SbKnY*NH5E&SnfqwqiMR+7T@Jp6{(O(r*_#ex40$Se3!}%Y-BTpLmALrmj zpHrT|uRaSu{(`cKy)|Iia1HjQ|FV2u8`l5%mOOcneGh)`(*M1RRy)A3LD~=7KCJx@ z`q&@VUT?lAytj{P|JPmir{Mn`2|bQxGPdSrRZU z-~p*q?wz7r-}P!5T#&8@ZsgZfA`v=)-SBQ+Y@bbfsvJ01-=vFS6`P(dLH*ct zyDM}lYwnr0TZ0o4oV<*N?gwQCCbUZTG^FFvxydN&Qc9#6Skf02w&P3{d~0{zv_f`XBYbZ*UP) z`hSVILvA^k5*=v@>3>Y#*vGG34V zcc|z{`ro0m1WT*S>ltW<)c?*w=VlYh02v?yBQQYykNO|=Kk9$f|ET|UeLjcZe;Hvc zWs&h^fcoF~UK`eo44gCrq5e13@dB16+aGyu?%;ELhSb!sBcF?!zpjP{d*wfwLHdE? z8_GRWBK1G{S!jO$S@sYHGC&5%z_>C%{g3(|^*`!=)c>geQU4p)QJVESE(6s6j>{m+ zlY!IAKt{0@P4V#a2&+DtSn*LPM_m8$$mFJ@u0tTdDgeoUo)WFjvG>n}!8 za@h43w+`5M6&?1gurAks^p04A%OkA6`1>yVM>N+r`%%|_>^{i^)_;_`?EZe!i8^C@ zE4ERv-Ew@m0ewEHmiU)iG= z$N(821LMj7^viyg?)uvGC&5%z-eHB z`XBW_>VMS#sQ*#_qyBdqMr_t`6b7jOjlvqskb$$oz;v@2(zBcTAHI0a@4uXlZp~JZ t0Wv@aMrMHeAN4=#f7Jh||55*={x|Y4%7SCg0QJ8yzd)=W88|@({tuJ`t=9km diff --git a/mus151/Makefile b/mus151/Makefile index d9be644..2ba3df1 100644 --- a/mus151/Makefile +++ b/mus151/Makefile @@ -1,11 +1,8 @@ # MUS151 Makefile - Global version for Unix systems which have GNU -# Makefile utilities installed. -# -# This is an example project Makefile, to demonstrate how one might -# develop an STK project that resides apart from the core STK -# distribution. It is highly recommended that you keep your -# personal STK projects separate from the core distribution, to -# make upgrading to newer releases more simple. +# Makefile utilities installed. If you do not have the GNU Makefile +# utilities, I suggest you download them. If that is not possible, +# look at syntmono/Makefile.sgi to see what has to be done to get +# things to compile. # # by Gary P. Scavone # CCRMA, Stanford University, 1998. @@ -16,27 +13,26 @@ OS = $(shell uname) # location in your system. The following definition corresponds # to an STK project directory that is a subdirectory of the core # STK distribution. -STK_PATH = ../ +STK_PATH = ../STK/ -O_FILES = Object.o Envelope.o RawWave.o RawLoop.o \ +O_FILES = Object.o Envelope.o WvIn.o RawWvIn.o \ SKINI11.o swapstuf.o WvOut.o \ RTWvOut.o RTSoundIO.o \ \ - TwoOsc.o + TwoOsc.o threads.o RM = /bin/rm ifeq ($(OS),IRIX) # These are for SGI INSTR = MUS151 - CC = CC -O # -g -fullwarn -D__SGI_CC__ + CC = CC -O2 -D__OS_IRIX_ # -g -fullwarn -D__SGI_CC__ LIBRARY = -L/usr/sgitcl/lib -laudio -lmd -lm endif ifeq ($(OS),Linux) # These are for Linux INSTR = MUS151 - CC = gcc -O3 # -g -pg -O3 + CC = g++ -O3 -D__OS_Linux_ # -g LIBRARY = -lpthread -lm -# LIBRARY = /lib/libpthread.so.0 -lm endif %.o : $(STK_PATH)%.cpp @@ -50,6 +46,9 @@ MUS151: MUS151.cpp $(O_FILES) TwoOsc.o : TwoOsc.cpp $(CC) -c TwoOsc.cpp +threads.o : threads.cpp + $(CC) -c threads.cpp + all: $(INSTR) clean : diff --git a/mus151/README-mus151.txt b/mus151/README-mus151.txt index 29e3f5d..38d46ef 100644 --- a/mus151/README-mus151.txt +++ b/mus151/README-mus151.txt @@ -1,15 +1,21 @@ +STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ +Version 3.0 + +By Perry R. Cook, 1995-99 +and Gary P. Scavone, 1997-99. + MUS151: An example project using STK By Gary P. Scavone -CCRMA, Stanford University, 1998. +CCRMA, Stanford University, 1999. -It is highly recommended that you keep your personal STK projects separate from the STK core distribution, to make upgrading to newer releases more simple. The files in this directory demonstrate how one might manage this. +This directory contains a simple two-oscillator program that can be used to demonstrate psychoacoustic masking effects. It is highly recommended that you keep your personal STK projects separate from the STK core distribution, to make upgrading to newer releases more simple. The files in this directory demonstrate how one might manage this. This simple project, named MUS151 for the pyschoacoustics class taught at CCRMA, plays two sine waves and allows independent control of their amplitudes and frequencies. The file MUS151.cpp is essentially the same as syntmono.cpp, though simplified to a great extent because of its more specific use. In particular, only one "instrument" is currently supported (TwoOsc) and only real-time output is used (thus, this project cannot be compiled for NeXTStep). A simple Tcl/Tk GUI has been created to control the instrument. The GUI and STK project can be run under Linux and SGI using the simple GUITwoOsc executable script. -The mus151 project directory has been distributed as a subdirectory of the core STK file directory. In this location, the project should compile without any problems (on Linux, SGI, and Win platforms). Should you choose to relocate this directory, several path variables will need to be updated as described below. +The mus151 project directory has been distributed as a separate directory from the core STK file directory. In this location, the project should compile without any problems (on Linux, SGI, and Win platforms). Should you choose to relocate this directory, several path variables will need to be updated as described below. -Three "personal" STK files were created for this project: MUS151.cpp and the TwoOsc class files TwoOsc.h and TwoOsc.cpp. A specific Tcl/Tk GUI script was written (TwoOsc.tcl) and is located in the "tcl" subdirectory. Depending on where you choose to locate the mus151 directory, several #include statements may need to be updated to reflect the location of the STK core distribution. For example, mus151/ is currently a subdirectory of the STK core files, so the various core STK header files are referenced in TwoOsc.h as: #include "../Instrmnt.h". You could move the mus151 files to a completely different point on your directory tree, but you would have to update those statements to correctly reference your core files. Likewise, the "sinewave.raw" file must be correctly referenced in TwoOsc.cpp. +Three "personal" STK files were created for this project: MUS151.cpp and the TwoOsc class files TwoOsc.h and TwoOsc.cpp. A specific Tcl/Tk GUI script was written (TwoOsc.tcl) and is located in the "tcl" subdirectory. Depending on where you choose to locate the mus151 directory, several #include statements may need to be updated to reflect the location of the STK core distribution. For example, mus151/ is currently outside the STK core files directory, so the various core STK header files are referenced in TwoOsc.h as: #include "../STK/Instrmnt.h". You could move the mus151 files to a completely different point on your directory tree, but you would have to update those statements to correctly reference your core files. Likewise, the RAWWAVE_PATH define statement in Object.h must be properly set for the "sinewave.raw" file to be correctly referenced in TwoOsc.cpp. The unix Makefile must also be updated to correctly reference the STK core distribution. A variable, STK_PATH, has been created to simplify this procedure. Note that all STK core object files are referenced with a single, general statement. All personal object files need their own dependency definitions. diff --git a/mus151/TwoOsc.cpp b/mus151/TwoOsc.cpp index 6f51387..9a938da 100644 --- a/mus151/TwoOsc.cpp +++ b/mus151/TwoOsc.cpp @@ -13,8 +13,12 @@ TwoOsc :: TwoOsc() envelope = new Envelope; /* Envelope to avoid clicks */ + // Concatenate the STK RAWWAVE_PATH to the rawwave file + char file[128]; + strcpy(file, RAWWAVE_PATH); for (i=0; i<2; i++) { - osc[i] = new RawLoop("../rawwaves/sinewave.raw"); + osc[i] = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); + strcpy(file, RAWWAVE_PATH); osc[i]->normalize(); osc[i]->setFreq((MY_FLOAT) 200.0); amps[i] = 0.2; diff --git a/mus151/TwoOsc.h b/mus151/TwoOsc.h index 400c42b..53d1ad7 100644 --- a/mus151/TwoOsc.h +++ b/mus151/TwoOsc.h @@ -8,16 +8,14 @@ #if !defined(__TwoOsc_h) #define __TwoOsc_h -//#include "../Instrmnt.h" -#include "../Envelope.h" -#include "../RawLoop.h" - +#include "../STK/Envelope.h" +#include "../STK/RawWvIn.h" class TwoOsc : public Object { protected: Envelope *envelope; - RawLoop *osc[2]; + RawWvIn *osc[2]; MY_FLOAT lastOutput; MY_FLOAT amps[2]; diff --git a/miditabl.h b/mus151/miditabl.h similarity index 97% rename from miditabl.h rename to mus151/miditabl.h index 8240175..8938646 100644 --- a/miditabl.h +++ b/mus151/miditabl.h @@ -1,4 +1,4 @@ -#include "Object.h" +#include "../STK/Object.h" double __MIDI_To_Pitch[128] = { 8.18,8.66,9.18,9.72,10.30,10.91,11.56,12.25, diff --git a/mus151/tcl/TwoWaves.tcl b/mus151/tcl/TwoWaves.tcl index 4312cef..dcc48ae 100644 --- a/mus151/tcl/TwoWaves.tcl +++ b/mus151/tcl/TwoWaves.tcl @@ -1,3 +1,7 @@ +# Tcl/Tk Demo GUI for the Synthesis Toolkit (STK) +# by Gary P. Scavone, CCRMA, Stanford University, 1999. + +# Set initial control values set pitch 64.0 set osc1on 1 set osc2on 1 @@ -93,6 +97,9 @@ checkbutton .onOff.2 -text "Play Osc 2" -variable osc2on -relief flat \ pack .onOff.1 .onOff.2 -padx 5 pack .onOff -side right -padx 5 -pady 10 +# Bind an X windows "close" event with the Exit routine +bind . +myExit + proc myExit {} { global outID puts $outID [format "NoteOff 0.0 1 64 127" ] @@ -149,7 +156,9 @@ proc setPlayStatus {controlNum value } { set d .socketdialog proc setComm {} { - global outID commtype d + global outID + global commtype + global d if {$commtype == "stdout"} { if { [string compare "stdout" $outID] } { set i [tk_dialog .dialog "Break Socket Connection?" {You are about to break an existing socket connection ... is this what you want to do?} "" 0 Cancel OK] @@ -161,21 +170,30 @@ proc setComm {} { } } elseif { ![string compare "stdout" $outID] } { set sockport 2001 + set sockhost localhost toplevel $d wm title $d "STK Client Socket Connection" wm resizable $d 0 0 grab $d - label $d.message -text "Specify a socket port number below (if different than the STK default of 2001) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ -background white -font {Helvetica 10 bold} \ -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} frame $d.sockport - entry $d.sockport.entry -width 6 - label $d.sockport.text -text "Socket Port Number:" \ + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ -font {Helvetica 10 bold} pack $d.message -side top -padx 5 -pady 10 - pack $d.sockport.text -side left -padx 1 -pady 10 - pack $d.sockport.entry -side right -padx 5 -pady 10 - pack $d.sockport -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost $d.sockport.entry insert 0 $sockport frame $d.buttons button $d.buttons.cancel -text "Cancel" -bg grey66 \ @@ -184,8 +202,10 @@ proc setComm {} { destroy $d } button $d.buttons.connect -text "Connect" -bg grey66 \ -command { + set sockhost [$d.sockhost.entry get] set sockport [$d.sockport.entry get] - set err [catch {socket localhost $sockport} outID] + set err [catch {socket $sockhost $sockport} outID] + if {$err == 0} { destroy $d } else { diff --git a/mus151/threads.cpp b/mus151/threads.cpp new file mode 100644 index 0000000..7e7d640 --- /dev/null +++ b/mus151/threads.cpp @@ -0,0 +1,282 @@ +// Thread functions for use with syntmono. +// +// Gary P. Scavone, 1999. + +#include "threads.h" + +#if defined(__STK_REALTIME_) + +#define SERVICE_PORT 2001 // Socket Port ID number + +// Do OS dependent declarations and includes +#if defined(__OS_IRIX_) +#include +#include +#include +#include +#include +#include +#include + +pid_t string_thread; + +#elif defined(__OS_Linux_) +#include +#include +#include +#include +#include +#include + +pthread_t string_thread; + +#elif defined(__OS_Win_) +#include +#include + +unsigned long string_thread; + +#endif + + +// The thread function definition protocols are slightly +// different under Irix, Linux, and Windoze. +#if defined(__OS_IRIX_) + +void newStringByPipe(void *) + +#elif defined(__OS_Linux_) + +void *newStringByPipe(void *) + +#elif defined(__OS_Win_) + +void newStringByPipe(void *) + +#endif + +{ + extern int numStrings, notDone; + extern char **inputString; + int i; + + // Malloc inputString. + inputString = (char **) malloc(MAX_IN_STRINGS * sizeof(char *)); + for ( i=0;i MAX_IN_STRINGS) { + fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); + numStrings--; + } + inOne++; + if (inOne == MAX_IN_STRINGS) inOne = 0; + } + } + + // Free inputString. + for ( i=0;i maxfd) maxfd = accept_id; + FD_CLR(soc_id, &rmask); + } + for (fd=0;fd<=maxfd;fd++) { // look for other sockets with data + if (FD_ISSET(fd, &rmask)) { // process the data + parsing = 1; + while (parsing) { + i = recv(fd, socBuf, STRING_LEN,0); + if (i==0) { + printf("Closing a socket connection.\n"); + FD_CLR(fd, &mask); +#if defined(__OS_Win_) + closesocket(fd); +#else + close(fd); +#endif + parsing = 0; + } + n = 0; + while (n < i) { + inputString[inOne][m++] = socBuf[n]; + if (socBuf[n++] == '\n') { + if (inputString[inOne][2] == 'i' && inputString[inOne][3] == 't' + && inputString[inOne][1] == 'x' && inputString[inOne][0] == 'E') { + notDone = 0; + n = i; + parsing = 0; + } + else { + m = 0; + if (n >= i) parsing = 0; + numStrings++; + if (numStrings > MAX_IN_STRINGS) { + fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); + numStrings--; + } + inOne++; + if (inOne == MAX_IN_STRINGS) inOne = 0; + memset(inputString[inOne], 0, STRING_LEN); + } + } + } + } + } + } + } +#if defined(__OS_Win_) // Stupid Windoze only stuff + closesocket(soc_id); + WSACleanup(); +#else + shutdown(soc_id,0); +#endif + + // Free inputString. + for ( i=0;iE@=9tf|1jz`!BEVzlS~|NpYi{=Pst1|bGUAo+knz=8W0kO2b0 R!G114T_+&CQF5q<002d84YvRQ diff --git a/scores/tstloops.sco b/scores/tstloops.sco deleted file mode 100644 index fc05d88..0000000 --- a/scores/tstloops.sco +++ /dev/null @@ -1,60 +0,0 @@ -/******* Pitch and amplitude Test Loops ********/ - -/* for (j=0;j<12;j++) { - printf("%lf\n",55.0 * pow(1.414,j)); - instrument.noteOn(55.0 * pow(1.414,j),0.1); - for (i=0;i<15000;i++) output.tick(instrument.tick()); - instrument.noteOff(0.1); - for (i=0;i<5000;i++) output.tick(instrument.tick()); - instrument.noteOn(55.0 * pow(1.414,j),0.4); - for (i=0;i<15000;i++) output.tick(instrument.tick()); - instrument.noteOff(0.1); - for (i=0;i<5000;i++) output.tick(instrument.tick()); - instrument.noteOn(55.0 * pow(1.414,j),0.7); - for (i=0;i<15000;i++) output.tick(instrument.tick()); - instrument.noteOff(0.1); - for (i=0;i<5000;i++) output.tick(instrument.tick()); - instrument.noteOn(55.0 * pow(1.414,j),1.0); - for (i=0;i<15000;i++) output.tick(instrument.tick()); - instrument.noteOff(0.1); - for (i=0;i<5000;i++) output.tick(instrument.tick()); - } -*/ - -/************ Controller Test Loops ***************/ - for (j=0;j<5;j++) { - instrument.noteOn(440,1.0); - instrument.controlChange(MOD_WHEEL,3 + (j*31)); - for (i=0;i<20000;i++) { - output.tick(instrument.tick()); - } - } - for (j=0;j<5;j++) { - instrument.noteOn(440,1.0); - instrument.controlChange(CONTROL3,3 + (j*31)); - for (i=0;i<20000;i++) { - output.tick(instrument.tick()); - } - } - instrument.controlChange(CONTROL3,64); - instrument.controlChange(MOD_WHEEL,0); - for (j=0;j<5;j++) { - instrument.controlChange(CONTROL1,3 + (j*31)); - instrument.noteOn(440,1.0); /* some need it before, some after */ - instrument.controlChange(CONTROL1,3 + (j*31)); - for (i=0;i<20000;i++) { - output.tick(instrument.tick()); - } - } - instrument.controlChange(CONTROL1,64); - for (j=0;j<5;j++) { - instrument.controlChange(CONTROL2,3 + (j*31)); - instrument.noteOn(440,1.0); /* some need it before, some after */ - instrument.controlChange(CONTROL2,3 + (j*31)); - for (i=0;i<20000;i++) { - output.tick(instrument.tick()); - } - } - instrument.controlChange(CONTROL2,64); - - diff --git a/swapstuf.h b/swapstuf.h deleted file mode 100644 index c32380d..0000000 --- a/swapstuf.h +++ /dev/null @@ -1,3 +0,0 @@ -int SwapInt(int inf); -short SwapShort(short inf); -float SwapFloat(float inf); diff --git a/syntmono.cpp b/syntmono.cpp deleted file mode 100644 index c616b2d..0000000 --- a/syntmono.cpp +++ /dev/null @@ -1,603 +0,0 @@ -/************** Test Main Program Individual Voice *********************/ - -#include "miditabl.h" -#include "WvOut.h" -#include "SndWvOut.h" -#include "WavWvOut.h" -#include "MatWvOut.h" -#ifndef __NeXT_ -#include "RTWvOut.h" -#endif -#include "SKINI11.h" -#include "Instrmnt.h" -#include "Marimba.h" -#include "Vibraphn.h" -#include "AgogoBel.h" -#include "Plucked.h" -#include "Mandolin.h" -#include "Clarinet.h" -#include "Flute.h" -#include "Brass.h" -#include "Bowed.h" -#include "Rhodey.h" -#include "Wurley.h" -#include "TubeBell.h" -#include "HeavyMtl.h" -#include "PercFlut.h" -#include "BeeThree.h" -#include "FMVoices.h" -#include "VoicForm.h" -#include "Moog1.h" -#include "Simple.h" -#include "DrumSynt.h" -#include "Shakers.h" -#include "Reverb.h" -#include "NRev.h" -#include "PRCRev.h" -#include "JCRev.h" - -#define NUM_INSTS 30 - -char insts[NUM_INSTS][10] = { "Marimba", "Vibraphn", "AgogoBel", "Plucked", - "Mandolin", "Clarinet", "Flute", "Brass", - "Bowed", "Rhodey", "Wurley", "TubeBell", - "HeavyMtl", "PercFlut", "BeeThree", "FMVoices", - "Moog1", "Simple", "VoicForm", "TKSinger", - "Shakers", "Maraca", "Cabasa", "Sekere", - "Guiro", "Waterdrp", "Bamboo", "Tambourn", - "Sleighbl", "DrumSynt"}; - -#include "SKINI11.msg" - -#define INSTR_LEN 60 - -int numStrings = 0; -int notDone = 1; - -#if defined(__SGI_REALTIME_) - -#include -#include -#include - -char inputString[MAX_IN_STRINGS][INSTR_LEN]; -pid_t string_thread; - -void newString(void *) -{ - int inOne = 0; - while (notDone) { - fgets(inputString[inOne],INSTR_LEN,stdin); - if (inputString[inOne][0] == 'E' && inputString[inOne][1] == 'x') { - notDone = 0; - } - else { - numStrings++; - if (numStrings > MAX_IN_STRINGS) { - fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); - numStrings--; - } - inOne++; - if (inOne == MAX_IN_STRINGS) inOne = 0; - } - } -} - -#elif defined(__USS_REALTIME_) - -#include -//#include - -char inputString[MAX_IN_STRINGS][INSTR_LEN]; -pthread_t string_thread; - -void *newString(void *) -{ - int inOne = 0; - while (notDone) { - fgets(inputString[inOne],INSTR_LEN,stdin); - if (inputString[inOne][0] == 'E' && inputString[inOne][1] == 'x') { - notDone = 0; - } - else { - numStrings++; - if (numStrings > MAX_IN_STRINGS) { - fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); - numStrings--; - } - inOne++; - if (inOne == MAX_IN_STRINGS) inOne = 0; - } - } -} - -#elif (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) - -#include -#include - -char inputString[MAX_IN_STRINGS][INSTR_LEN]; -unsigned long string_thread; - -#define SERVICE_PORT 2001 - -void newString(void *) -{ - int inOne = 0, i=0, m=0, n; - SOCKET soc_id, accept_id; - WSADATA wsaData; - int nRet; - struct sockaddr_in sockname; - WORD wVersionRequested = MAKEWORD(1,1); - char socBuf[INSTR_LEN]; - - nRet = WSAStartup(wVersionRequested, &wsaData); - if (wsaData.wVersion != wVersionRequested) - { - fprintf(stderr,"\n Wrong version\n"); - exit(0); - } - - /* Create the server-side socket */ - soc_id = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); - if(soc_id == INVALID_SOCKET) { - fprintf(stderr,"Couldn't create socket!\n"); - exit(0); - } - - sockname.sin_family=AF_INET; - sockname.sin_addr.s_addr=INADDR_ANY; - sockname.sin_port=htons(SERVICE_PORT); - - /* Bind socket to the appropriate port and interface (INADDR_ANY) */ - if (bind(soc_id,(LPSOCKADDR)&sockname,sizeof(sockname))==SOCKET_ERROR) { - fprintf(stderr,"Couldn't bind socket!\n"); - exit(0); - } - - /* Listen for incoming connections */ - printf("Listening for socket on port %d\n", SERVICE_PORT); - if (listen(soc_id,1)==SOCKET_ERROR) { - fprintf(stderr,"Couldn't set up listen on socket!\n"); - exit(0); - } - - /* Accept and service one incoming connection request */ - accept_id=accept(soc_id,NULL,NULL); - if (accept_id==INVALID_SOCKET) { - fprintf(stderr,"Couldn't accept incoming connection on socket!\n"); - exit(0); - } - - memset(socBuf, 0, sizeof(socBuf)); - - printf("Socket connection made ... ready to receive SKINI messages.\n"); - while (notDone) { - i = recv(accept_id, socBuf, INSTR_LEN, 0); - if (i==0) notDone = 0; - n = 0; - while (n < i) { - inputString[inOne][m++] = socBuf[n]; - if (socBuf[n++] == '\n') { - if (inputString[inOne][0] == 'E' && inputString[inOne][1] == 'x') { - notDone = 0; - n = i; - } - else { - m = 0; - numStrings++; - if (numStrings > MAX_IN_STRINGS) { - fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); - numStrings--; - } - inOne++; - if (inOne == MAX_IN_STRINGS) inOne = 0; - } - memset(inputString[inOne], 0, INSTR_LEN); - } - } - } - closesocket(accept_id); - closesocket(soc_id); - WSACleanup(); - printf("Socket connection closed.\n"); -} - -#else -char inputString[1][INSTR_LEN]; -#endif - -/* Error function in case of incorrect command-line argument specifications */ -void errorf(char *func) { - int i,j; - - printf("\nuseage: %s Instr flag(s) \n",func); - printf(" where flag = -s for .snd output file,\n"); - printf(" -w for .wav output file,\n"); - printf(" -m for .mat output file,\n"); -#if !defined(__NeXT_) - printf(" -r for realtime output,\n"); - printf(" -i for realtime input (versus scorefile),\n"); -#endif - printf(" and Instr = one of these:\n"); - for (i=0;i are not specified,\n"); - printf("default names will be indicated. Each flag must\n"); - printf("include its own '-' sign.\n\n"); - exit(0); -} - -void main(int argc,char *argv[]) -{ - long i, j, w, synlength; - int type, rtInput = 0; - int numOuts = 0, outOne = 0; - char *fin; - char flags[15]; - MY_FLOAT reverbTime = 0.5; /* in seconds */ - MY_FLOAT temp, byte3, lastPitch, outSample; - char fileName[256]; - WvOut **output; - Instrmnt *instrument; - Reverb *reverb; - SKINI11 *score; - - /* Check the command-line arguments for errors and to determine - * the number of WvOut objects to be instantiated. - */ - if (argc > 2 && argc < 11) { - i = 2; - j = 0; - while (i < argc) { - if (argv[i][0] == '-') { - if ( (argv[i][1] == 'r') || (argv[i][1] == 's') || - (argv[i][1] == 'w') || (argv[i][1] == 'm') ) - numOuts++; - else if (argv[i][1] != 'i') errorf(argv[0]); - flags[j] = argv[i][1]; - j++; - } - i++; - } - /* Check for multiple flags of the same type */ - for (i=0; i<=j; i++) { - w = i+1; - while (w <= j) { - if (flags[i] == flags[w]) { - printf("\nError: Multiple command line flags of the same type specified.\n\n"); - errorf(argv[0]); - } - w++; - } - } - /* Check number of outputs */ - if (numOuts > 0) output = (WvOut **) malloc(numOuts * sizeof(WvOut *)); - else errorf(argv[0]); - } - else errorf(argv[0]); - - /* Parse the command-line flags and instantiate WvOut objects. */ - i = 2; - j = 0; - while (i < argc) - { - if (argv[i][0] == '-') - { - switch(argv[i][1]) - { - case 'i': -#if !defined(__NeXT_) - rtInput = 1; - break; -#else - errorf(argv[0]); -#endif - - case 'r': -#if !defined(__NeXT_) - output[j] = new RTWvOut(); - j++; - break; -#else - errorf(argv[0]); -#endif - - case 'w': - if ((i+1 < argc) && argv[i+1][0] != '-') - strcpy(fileName,argv[i+1]); - else strcpy(fileName,"testwav"); - output[j] = new WavWvOut(2,fileName); - j++; - break; - - case 's': - if ((i+1 < argc) && argv[i+1][0] != '-') - strcpy(fileName,argv[i+1]); - else strcpy(fileName,"testsnd"); - output[j] = new SndWvOut(2,fileName); - j++; - break; - - case 'm': - if ((i+1 < argc) && argv[i+1][0] != '-') - strcpy(fileName,argv[i+1]); - else strcpy(fileName,"testmat"); - output[j] = new MatWvOut(2,fileName); - j++; - break; - - default: - errorf(argv[0]); - break; - } - } - i++; - } - - /* Parse the Instrument command-line argument. */ - if (!strcmp(argv[1],"Simple")) instrument = new Simple; - else if (!strcmp(argv[1],"Clarinet")) instrument = new Clarinet(50.0); - else if (!strcmp(argv[1],"Flute")) instrument = new Flute(50.0); - else if (!strcmp(argv[1],"Brass")) instrument = new Brass(50.0); - else if (!strcmp(argv[1],"Bowed")) instrument = new Bowed(50.0); - else if (!strcmp(argv[1],"Plucked")) instrument = new Plucked(50.0); - else if (!strcmp(argv[1],"Mandolin")) instrument = new Mandolin(50.0); - else if (!strcmp(argv[1],"Marimba")) instrument = new Marimba; - else if (!strcmp(argv[1],"Vibraphn")) instrument = new Vibraphn; - else if (!strcmp(argv[1],"AgogoBel")) instrument = new AgogoBel; - else if (!strcmp(argv[1],"Rhodey")) instrument = new Rhodey; - else if (!strcmp(argv[1],"Wurley")) instrument = new Wurley; - else if (!strcmp(argv[1],"TubeBell")) instrument = new TubeBell; - else if (!strcmp(argv[1],"HeavyMtl")) instrument = new HeavyMtl; - else if (!strcmp(argv[1],"PercFlut")) instrument = new PercFlut; - else if (!strcmp(argv[1],"BeeThree")) instrument = new BeeThree; - else if (!strcmp(argv[1],"Moog1")) instrument = new Moog1; - else if (!strcmp(argv[1],"FMVoices")) instrument = new FMVoices; - else if (!strcmp(argv[1],"VoicForm")) instrument = new VoicForm; - else if (!strcmp(argv[1],"DrumSynt")) instrument = new DrumSynt; - else if (!strcmp("Shakers",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 0); - } - else if (!strcmp("Maraca",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 0); - } - else if (!strcmp("Sekere",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 1); - } - else if (!strcmp("Cabasa",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 2); - } - else if (!strcmp("Bamboo",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 3); - } - else if (!strcmp("Waterdrp",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 4); - } - else if (!strcmp("Tambourn",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 5); - } - else if (!strcmp("Sleighbl",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 6); - } - else if (!strcmp("Guiro",argv[1])) { - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 7); - } - else { - errorf(argv[0]); - } - - score = new SKINI11(); - reverb = new PRCRev(reverbTime); - reverb->setEffectMix(0.2); - - /* If using realtime input, setup the input thread. */ -#if defined(__SGI_REALTIME_) - if (rtInput) { - string_thread = sproc(newString, PR_SALL); - if (string_thread == -1) { - fprintf(stderr, "unable to create input thread...aborting.\n"); - exit(-1); - } - instrument->noteOn(200.0,0.1); - } -#elif defined(__USS_REALTIME_) - if (rtInput) { - if (pthread_create(&string_thread, NULL, newString, NULL)) - { - fprintf(stderr, "unable to create input thread...aborting.\n"); - exit(-1); - } - instrument->noteOn(200.0,0.1); - } -#elif (defined(__WINDS_REALTIME_) || defined(__WINMM_REALTIME_) ) - if (rtInput) { - string_thread = _beginthread(newString, 0, NULL); - if (string_thread == -1) { - fprintf(stderr, "Unable to create exit thread.\n"); - printf("Exiting MD2SKINI process.\n"); - exit(0); - } - instrument->noteOn(200.0,0.1); - } -#endif - - /* Finally ... the runtime loop begins! */ - notDone = 1; - synlength = RT_BUFFER_SIZE; - while(notDone || numStrings) { - if (rtInput) { - if (numStrings > 1) synlength = (long) RT_BUFFER_SIZE / numStrings; - else synlength = RT_BUFFER_SIZE; - for ( i=0; i 1) { - outSample = reverb->tick(instrument->tick()); - for ( j=0; jtick(outSample); - } - else output[0]->tick(reverb->tick(instrument->tick())); - } - } - else { - fin = fgets(inputString[0],INSTR_LEN,stdin); - if (fin == NULL) notDone = 0; - else { - numStrings++; - } - } - if (numStrings) { - score->parseThis(inputString[outOne]); - type = score->getType(); - if (type > 0) { - if (temp = score->getDelta()) { /* SKINI score file */ - synlength = (long) (temp * SRATE); - for ( i=0; i 1) { - outSample = reverb->tick(instrument->tick()); - for ( j=0; jtick(outSample); - } - else output[0]->tick(reverb->tick(instrument->tick())); - } - synlength = 0; - } - if (type == __SK_NoteOn_ ) { - if (( byte3 = score->getByteThree() ) == 0) - instrument->noteOff(byte3*NORM_7); - else { - j = (int) score->getByteTwo(); - temp = __MIDI_To_Pitch[j]; - lastPitch = temp; - instrument->noteOn(temp,byte3*NORM_7); - } - } - else if (type == __SK_NoteOff_) { - byte3 = score->getByteThree(); - instrument->noteOff(byte3*NORM_7); - } - else if (type == __SK_ControlChange_) { - j = (int) score->getByteTwo(); - byte3 = score->getByteThree(); - instrument->controlChange(j,byte3); - } - else if (type == __SK_AfterTouch_) { - j = (int) score->getByteTwo(); - instrument->controlChange(128,j); - } - else if (type == __SK_PitchBend_) { - temp = score->getByteTwo(); - j = (int) temp; - temp -= j; - lastPitch = __MIDI_To_Pitch[j] * pow(2.0,temp / 12.0) ; - instrument->setFreq(lastPitch); - } - else if (type == __SK_ProgramChange_) { - instrument->noteOff(1.0); - for (i=0;i<4096;i++) { - if (numOuts > 1) { - outSample = reverb->tick(instrument->tick()); - for ( j=0; jtick(outSample); - } - else output[0]->tick(reverb->tick(instrument->tick())); - } - delete instrument; - j = (int) score->getByteTwo(); - if (j==0) instrument = new Clarinet(50.0); - else if (j==1) instrument = new Flute(50.0); - else if (j==2) instrument = new Brass(50.0); - else if (j==3) instrument = new Bowed(50.0); - - else if (j==4) instrument = new Plucked(50.0); - else if (j==5) instrument = new Mandolin(50.0); - else if (j==6) instrument = new Marimba; - else if (j==7) instrument = new Vibraphn; - else if (j==8) instrument = new AgogoBel; - else if (j==9) instrument = new Rhodey; - else if (j==10) instrument = new Wurley; - else if (j==11) instrument = new TubeBell; - - else if (j==12) instrument = new HeavyMtl; - else if (j==13) instrument = new PercFlut; - else if (j==14) instrument = new BeeThree; - else if (j==15) instrument = new Moog1; - - else if (j==16) instrument = new FMVoices; - else if (j==17) instrument = new VoicForm; - - else if (j==18) instrument = new DrumSynt; - - else if (j==19) { /* "Maraca" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 0); - } - else if (j==20) { /* "Sekere" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 1); - } - else if (j==21) { /* "Cabasa" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 2); - } - else if (j==22) { /* "Bamboo" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 3); - } - else if (j==23) { /* "Waterdrp" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 4); - } - else if (j==24) { /* "Tambourn" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 5); - } - else if (j==25) { /* "Sleighbl" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 6); - } - else if (j==26) { /* "Guiro" */ - instrument = new Shakers; - instrument->controlChange(__SK_ShakerInst_, 7); - } - instrument->noteOn(lastPitch, 0.5); - } - } - if (rtInput) { - outOne += 1; - if (outOne == MAX_IN_STRINGS) outOne = 0; - } - numStrings--; - } - } - - for (i=0;i 1) { - outSample = reverb->tick(instrument->tick()); - for ( j=0; jtick(outSample); - } - else output[0]->tick(reverb->tick(instrument->tick())); - } - - for ( i=0; i" with flags "" - -Creating temp file "C:\WINDOWS\TEMP\RSPF2B0.TMP" with contents -Creating command line "cl.exe @C:\WINDOWS\TEMP\RSPF2B0.TMP" -Creating temp file "C:\WINDOWS\TEMP\RSPF2B1.TMP" with contents -Creating command line "link.exe @C:\WINDOWS\TEMP\RSPF2B1.TMP" -Compiling... -ADSR.cpp -AgogoBel.cpp -BeeThree.cpp -BiQuad.cpp -Bowed.cpp -BowTabl.cpp -Brass.cpp -Clarinet.cpp -DCBlock.cpp -DLineA.cpp -DLineL.cpp -DLineN.cpp -DrumSynt.cpp -Envelope.cpp -Filter.cpp -Flute.cpp -FM4Alg3.cpp -FM4Alg4.cpp -FM4Alg5.cpp -FM4Alg6.cpp -FM4Alg8.cpp -FM4Op.cpp -FMVoices.cpp -FormSwep.cpp -HeavyMtl.cpp -Instrmnt.cpp -JCRev.cpp -JetTabl.cpp -LipFilt.cpp -Mandolin.cpp -Mandplyr.cpp -Marimba.cpp -MatWvOut.cpp -Modal4.cpp -Modulatr.cpp -Moog1.cpp -Noise.cpp -NRev.cpp -Object.cpp -OnePole.cpp -OneZero.cpp -PercFlut.cpp -Plucked.cpp -Plucked2.cpp -PRCRev.cpp -RawInterp.cpp -RawLoop.cpp -RawShot.cpp -RawWave.cpp -ReedTabl.cpp -Reverb.cpp -Rhodey.cpp -RTSoundIO.cpp -RTWvOut.cpp -Sampler.cpp -SamplFlt.cpp -Shakers.cpp -Simple.cpp -SingWave.cpp -SKINI11.cpp -SndWvOut.cpp -SubNoise.cpp -Swapstuf.cpp -Syntmono.cpp -TubeBell.cpp -TwoPole.cpp -TwoZero.cpp -Vibraphn.cpp -VoicForm.cpp -VoicMang.cpp -WavWvOut.cpp -Wurley.cpp -WvOut.cpp -Linking... - - - -syntmono.exe - 0 error(s), 0 warning(s) diff --git a/syntmono/GUIBowedBar b/syntmono/GUIBowedBar new file mode 100755 index 0000000..e05a908 --- /dev/null +++ b/syntmono/GUIBowedBar @@ -0,0 +1 @@ +wish < tcl/TCLBowedBar.tcl | syntmono BowedBar -r -ip diff --git a/syntmono/GUIDrums b/syntmono/GUIDrums new file mode 100755 index 0000000..ebcaf56 --- /dev/null +++ b/syntmono/GUIDrums @@ -0,0 +1 @@ +wish < tcl/TCLDrums.tcl | syntmono DrumSynt -r -ip diff --git a/syntmono/GUIPhysical b/syntmono/GUIPhysical new file mode 100755 index 0000000..4a6e8ea --- /dev/null +++ b/syntmono/GUIPhysical @@ -0,0 +1 @@ +wish < tcl/TCLPhys.tcl | syntmono Clarinet -r -ip diff --git a/syntmono/GUIPlukStruk b/syntmono/GUIPlukStruk new file mode 100755 index 0000000..817a059 --- /dev/null +++ b/syntmono/GUIPlukStruk @@ -0,0 +1 @@ +wish < tcl/TCLStruk.tcl | syntmono Mandolin -r -ip diff --git a/syntmono/GUIShakers b/syntmono/GUIShakers new file mode 100755 index 0000000..e13e0ad --- /dev/null +++ b/syntmono/GUIShakers @@ -0,0 +1 @@ +wish < tcl/TCLShakers.tcl | syntmono Shakers -r -ip diff --git a/syntmono/GUIVoice b/syntmono/GUIVoice new file mode 100755 index 0000000..a2c1ee3 --- /dev/null +++ b/syntmono/GUIVoice @@ -0,0 +1 @@ +wish < tcl/TCLVoice.tcl | syntmono FMVoices -r -ip diff --git a/TCLSpecs/MIDIPhysical b/syntmono/MIDIPhysical similarity index 85% rename from TCLSpecs/MIDIPhysical rename to syntmono/MIDIPhysical index 4f3d085..0a22b43 100755 --- a/TCLSpecs/MIDIPhysical +++ b/syntmono/MIDIPhysical @@ -1,2 +1 @@ -cd .. MD2SKINI | syntmono Clarinet -r -i diff --git a/TCLSpecs/MIDIPlukStruk b/syntmono/MIDIPlukStruk similarity index 85% rename from TCLSpecs/MIDIPlukStruk rename to syntmono/MIDIPlukStruk index a053831..5a1e9c9 100755 --- a/TCLSpecs/MIDIPlukStruk +++ b/syntmono/MIDIPlukStruk @@ -1,2 +1 @@ -cd .. MD2SKINI | syntmono Mandolin -r -i diff --git a/TCLSpecs/MIDIVoice b/syntmono/MIDIVoice similarity index 85% rename from TCLSpecs/MIDIVoice rename to syntmono/MIDIVoice index f6ee005..7da7222 100755 --- a/TCLSpecs/MIDIVoice +++ b/syntmono/MIDIVoice @@ -1,2 +1 @@ -cd .. MD2SKINI | syntmono FMVoices -r -i diff --git a/Makefile b/syntmono/Makefile similarity index 57% rename from Makefile rename to syntmono/Makefile index 01578a2..60455d6 100644 --- a/Makefile +++ b/syntmono/Makefile @@ -1,13 +1,16 @@ -# STK98 Makefile - Global version for Unix systems which have GNU +# STK Makefile - Global version for Unix systems which have GNU # Makefile utilities installed. If this Makefile does not work on # your system, try using the platform specific Makefiles (.sgi, # .next, and .linux). OS = $(shell uname) +# The following definition indicates the relative location of +# the core STK classes. +STK_PATH = ../STK/ + O_FILES = Object.o Envelope.o ADSR.o Noise.o SubNoise.o \ - RawWave.o RawShot.o RawLoop.o RawInterp.o \ - Modulatr.o Filter.o OneZero.o \ + Modulatr.o Filter.o OneZero.o TablLook.o \ OnePole.o TwoZero.o TwoPole.o DCBlock.o \ BiQuad.o DLineA.o DLineL.o DLineN.o VoicMang.o \ FormSwep.o BowTabl.o JetTabl.o ReedTabl.o \ @@ -15,48 +18,48 @@ O_FILES = Object.o Envelope.o ADSR.o Noise.o SubNoise.o \ FM4Alg5.o FM4Alg6.o FM4Alg8.o Plucked2.o \ SamplFlt.o Sampler.o SKINI11.o Simple.o \ SingWave.o VoicForm.o FMVoices.o swapstuf.o \ + utilities.o threads.o \ \ Instrmnt.o Marimba.o Vibraphn.o AgogoBel.o Shakers.o \ Plucked.o Mandolin.o Clarinet.o Flute.o Moog1.o \ Brass.o Bowed.o Rhodey.o Wurley.o TubeBell.o \ HeavyMtl.o PercFlut.o BeeThree.o DrumSynt.o \ \ - WvOut.o SndWvOut.o WavWvOut.o MatWvOut.o \ - Reverb.o PRCRev.o JCRev.o NRev.o + WvOut.o SndWvOut.o WavWvOut.o MatWvOut.o RawWvOut.o \ + WvIn.o SndWvIn.o WavWvIn.o MatWvIn.o RawWvIn.o \ + Reverb.o PRCRev.o JCRev.o NRev.o BowedBar.o RM = /bin/rm ifeq ($(OS),NEXTSTEP) # These are for NeXT - CC = cc -arch m68k -arch i386 -Wall + CC = cc -arch m68k -arch i386 -Wall -D__OS_NeXT_ INSTR = syntmono endif ifeq ($(OS),IRIX) # These are for SGI INSTR = MD2SKINI syntmono - CC = CC -O # -g -fullwarn -D__SGI_CC__ - O_FILES += RTWvOut.o RTSoundIO.o MIDIIO.o + CC = CC -O2 -D__OS_IRIX_ # -g -fullwarn -D__SGI_CC__ + O_FILES += RTWvOut.o RTSoundIO.o RTWvIn.o MIDIIO.o RTDuplex.o LIBRARY = -L/usr/sgitcl/lib -laudio -lmd -lm endif ifeq ($(OS),Linux) # These are for Linux INSTR = syntmono MD2SKINI - CC = gcc -O3 # -g -pg -O3 - O_FILES += RTWvOut.o RTSoundIO.o MIDIIO.o + CC = g++ -O3 -D__OS_Linux_ # -g -O3 -Wall + O_FILES += RTWvOut.o RTSoundIO.o RTWvIn.o MIDIIO.o RTDuplex.o LIBRARY = -lpthread -lm -# LIBRARY = /lib/libpthread.so.0 -lm endif -.SUFFIXES: .cpp -.cpp.o: Object.h - $(CC) -c $*.cpp +%.o : $(STK_PATH)%.cpp + $(CC) -c $(<) -o $@ all: $(INSTR) syntmono: syntmono.cpp $(O_FILES) $(CC) $(INCLUDE) -o syntmono syntmono.cpp $(O_FILES) $(LIBRARY) -MD2SKINI: MD2SKINI.cpp $(O_FILES) - $(CC) -o MD2SKINI MD2SKINI.cpp $(O_FILES) $(LIBRARY) +MD2SKINI: $(STK_PATH)MD2SKINI.cpp $(O_FILES) + $(CC) -o MD2SKINI $(STK_PATH)MD2SKINI.cpp Object.o MIDIIO.o $(LIBRARY) clean : rm *.o @@ -67,3 +70,11 @@ cleanIns : strip : strip $(INSTR) + +# $(O_FILES) : + +utilities.o: utilities.cpp + $(CC) -c utilities.cpp + +threads.o: threads.cpp + $(CC) -c threads.cpp \ No newline at end of file diff --git a/syntmono/Makefile.sgi b/syntmono/Makefile.sgi new file mode 100644 index 0000000..cd598e2 --- /dev/null +++ b/syntmono/Makefile.sgi @@ -0,0 +1,74 @@ +# STK Makefile - SGI solo version (non-GNU Makefile utilities version) + +# The following definition indicates the relative location of +# the core STK classes. +STK_PATH = ../STK/ + +O_FILES = $(STK_PATH)Object.o $(STK_PATH)Envelope.o $(STK_PATH)ADSR.o \ + $(STK_PATH)Noise.o $(STK_PATH)SubNoise.o $(STK_PATH)Modulatr.o \ + $(STK_PATH)Filter.o $(STK_PATH)OneZero.o $(STK_PATH)OnePole.o \ + $(STK_PATH)TwoZero.o $(STK_PATH)TwoPole.o $(STK_PATH)DCBlock.o \ + $(STK_PATH)BiQuad.o $(STK_PATH)DLineA.o $(STK_PATH)DLineL.o \ + $(STK_PATH)DLineN.o $(STK_PATH)VoicMang.o $(STK_PATH)FormSwep.o \ + $(STK_PATH)BowTabl.o $(STK_PATH)JetTabl.o $(STK_PATH)ReedTabl.o \ + $(STK_PATH)LipFilt.o $(STK_PATH)Modal4.o $(STK_PATH)FM4Op.o \ + $(STK_PATH)FM4Alg3.o $(STK_PATH)FM4Alg4.o $(STK_PATH)FM4Alg5.o \ + $(STK_PATH)FM4Alg6.o $(STK_PATH)FM4Alg8.o $(STK_PATH)Plucked2.o \ + $(STK_PATH)SamplFlt.o $(STK_PATH)Sampler.o $(STK_PATH)SKINI11.o \ + $(STK_PATH)Simple.o $(STK_PATH)SingWave.o $(STK_PATH)VoicForm.o \ + $(STK_PATH)FMVoices.o $(STK_PATH)swapstuf.o \ + \ + $(STK_PATH)Instrmnt.o $(STK_PATH)Marimba.o $(STK_PATH)Vibraphn.o \ + $(STK_PATH)AgogoBel.o $(STK_PATH)Shakers.o $(STK_PATH)Plucked.o \ + $(STK_PATH)Mandolin.o $(STK_PATH)Clarinet.o $(STK_PATH)Flute.o \ + $(STK_PATH)Moog1.o $(STK_PATH)Brass.o $(STK_PATH)Bowed.o \ + $(STK_PATH)Rhodey.o $(STK_PATH)Wurley.o $(STK_PATH)TubeBell.o \ + $(STK_PATH)HeavyMtl.o $(STK_PATH)PercFlut.o $(STK_PATH)BeeThree.o \ + $(STK_PATH)DrumSynt.o \ + \ + $(STK_PATH)WvOut.o $(STK_PATH)SndWvOut.o $(STK_PATH)WavWvOut.o \ + $(STK_PATH)MatWvOut.o $(STK_PATH)WvIn.o $(STK_PATH)SndWvIn.o \ + $(STK_PATH)WavWvIn.o $(STK_PATH)MatWvIn.o $(STK_PATH)RawWvIn.o \ + $(STK_PATH)Reverb.o $(STK_PATH)PRCRev.o $(STK_PATH)JCRev.o \ + $(STK_PATH)NRev.o $(STK_PATH)RTWvOut.o $(STK_PATH)RTWvIn.o \ + $(STK_PATH)RTSoundIO.o $(STK_PATH)MIDIIO.o $(STK_PATH)RTDuplex.o \ + $(STK_PATH)BowedBar.o + +O_UTIL_FILES = utilities.o threads.o + +RM = /bin/rm + +INSTR = MD2SKINI syntmono +CC = CC -O2 -D__OS_IRIX_ # -g -fullwarn -D__SGI_CC__ +LIBRARY = -L/usr/sgitcl/lib -laudio -lmd -lm + +.SUFFIXES: .cpp +.cpp.o: $(STK_PATH)Object.h $(O_FILES) + $(CC) -c -o $@ $< + +all: $(INSTR) + +syntmono: syntmono.cpp $(O_FILES) $(O_UTIL_FILES) + $(CC) $(INCLUDE) -o syntmono syntmono.cpp $(O_FILES) $(O_UTIL_FILES) $(LIBRARY) + +MD2SKINI: $(STK_PATH)MD2SKINI.cpp $(STK_PATH)Object.o $(STK_PATH)MIDIIO.o + $(CC) -o MD2SKINI $(STK_PATH)MD2SKINI.cpp $(STK_PATH)Object.o $(STK_PATH)MIDIIO.o $(LIBRARY) + +clean : + rm *.o + rm $(STK_PATH)*.o + rm $(INSTR) + +cleanIns : + rm $(INSTR) + +strip : + strip $(INSTR) + +# $(O_FILES) : + +utilities.o: utilities.cpp + $(CC) -c utilities.cpp + +threads.o: threads.cpp + $(CC) -c threads.cpp diff --git a/syntmono/Release/.placeholder b/syntmono/Release/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/STK98v2.dsw b/syntmono/STK.dsw similarity index 100% rename from STK98v2.dsw rename to syntmono/STK.dsw diff --git a/syntmono/STK.opt b/syntmono/STK.opt new file mode 100755 index 0000000000000000000000000000000000000000..d8c1d81058a6cece84143e80f514959dc09eb74a GIT binary patch literal 53760 zcmeI5-Bw#g6vro|V1O1WA|hHnRxPbslu*C0VoMTi_y|alh(>EZhBT6#la&)>ao1OI z`2;?J7p~>4K7!u*3NFR}PIAH_B{5-P2=za#e`Ze3p0nrtX1M6??BUm6F8}u34->zu zx-zG{>hFVo)#v)&gSMz#h86A~97x-#rH!vx&JlsXFjfQe=7lamy^zfL6Oa$;hX$ZQ zNapzx>LKV!2una+hDM;Lpi$^)NbH_LJq|q!O+e2<&qG(B7oZm*u?wTV3cU=y0=)`d zgC?Qt&<*G{NNnFgeG{63-h|$QrXfEx1Kone?j6+cLhnKEL$lBa&~4~LXbuv)JE-qM z^H2Z^LLq1Y`WU(giJfQ(?PX{Mia@K-8uSUY4n-lc`xNyi6oWp4wxG|UFQ7Q|B_wwD zQGX33pd^%n(vXI>p$sH;|8-ec7Jf>~z?++>i)tU9TQ6f+G0qre?KCTQ_P$R}UHZ`@ z@8#IV;Jv7Jl&uP2k;3~MU|B-DRA%AWHL4BOQ(mvKc4Zum?A+h?$b~FclzDi!)E@k6 zwAwGj@b5)8`8{dX|3z_A^&i5lXYsB=;Ur~9^?2lWReVcncUtVx(>NP2WN`rw{vGiCM)RyO2m}V=}t2G&yVdjY84-MyE>G?HfZ*9=WlNUiMJ! z!R5=!W9w|1eR*7WU)bEX4#(pj>PS$3t8-RC7mc-|mEKL2s&CTpMx}S%E7s2guStLe zNPq-LfCNZ@1W14cNPq-Lpj!zr{@blH!*P*7X9#TlREeFA*Mf34jaWD`<2Tkr%b~l` z&_(A)<P^Z}DqheQG$Brt%#|6M6g^z;HQ`(lYw>cOqer*X>}qU;ORa>4zd%>Q;41#1!@0TLhq5+DH*AOR8}0TLjAt|GwrUsp{B2SWlK zC(u{+h|Pbs-II*}we|O3xc{%?YnBd4fCNZ@1W14cNPq-LfCNZ@1ll9O_+NXxXiWl- H8-afSLeJ&( literal 0 HcmV?d00001 diff --git a/syntmono/STKdemo b/syntmono/STKdemo new file mode 100755 index 0000000..250acad --- /dev/null +++ b/syntmono/STKdemo @@ -0,0 +1 @@ +wish < tcl/TCLDemo.tcl | syntmono Clarinet -r -ip diff --git a/md2skini.dsp b/syntmono/md2skini.dsp similarity index 86% rename from md2skini.dsp rename to syntmono/md2skini.dsp index 7b75b6f..0a4e480 100644 --- a/md2skini.dsp +++ b/syntmono/md2skini.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__OS_Win_" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__OS_Win_" /YX /FD /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -84,31 +84,23 @@ LINK32=link.exe # Name "MD2SKINI - Win32 Debug" # Begin Source File -SOURCE=.\MD2SKINI.cpp +SOURCE=..\Stk\MD2SKINI.cpp # End Source File # Begin Source File -SOURCE=.\MIDIIO.cpp +SOURCE=..\Stk\MIDIIO.cpp # End Source File # Begin Source File -SOURCE=.\MIDIIO.h +SOURCE=..\Stk\MIDIIO.h # End Source File # Begin Source File -SOURCE=.\Miditabl.h +SOURCE=..\Stk\Object.cpp # End Source File # Begin Source File -SOURCE=.\Object.cpp -# End Source File -# Begin Source File - -SOURCE=.\Object.h -# End Source File -# Begin Source File - -SOURCE=.\SKINI11.msg +SOURCE=..\Stk\Object.h # End Source File # End Target # End Project diff --git a/md2skini.plg b/syntmono/md2skini.plg old mode 100644 new mode 100755 similarity index 60% rename from md2skini.plg rename to syntmono/md2skini.plg index 3864b57..90cdb8b --- a/md2skini.plg +++ b/syntmono/md2skini.plg @@ -1,26 +1,30 @@ --------------------Configuration: MD2SKINI - Win32 Release-------------------- -Begining build with project "D:\gary\Research\STK98v2\MD2SKINI.DSP", at root. +Begining build with project "C:\home\gary\stk\syntmono\MD2SKINI.DSP", at root. Active configuration is Win32 (x86) Console Application (based on Win32 (x86) Console Application) Project's tools are: - "32-bit C/C++ Compiler for 80x86" with flags "/nologo /MT /W3 /GX /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Release/MD2SKINI.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c " + "32-bit C/C++ Compiler for 80x86" with flags "/nologo /MT /W3 /GX /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__OS_Win_" /Fp"Release/MD2SKINI.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c " "Win32 Resource Compiler" with flags "/l 0x409 /d "NDEBUG" " "Browser Database Maker" with flags "/nologo /o"MD2SKINI.bsc" " "COFF Linker for 80x86" with flags "kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib dsound.lib Wsock32.lib /nologo /subsystem:console /incremental:no /pdb:"MD2SKINI.pdb" /machine:I386 /out:"MD2SKINI.exe" " "Custom Build" with flags "" "" with flags "" -Creating temp file "c:\windows\TEMP\RSPD220.TMP" with contents -Creating command line "cl.exe @c:\windows\TEMP\RSPD220.TMP" -Creating temp file "c:\windows\TEMP\RSPD221.TMP" with contents -Creating command line "link.exe @c:\windows\TEMP\RSPD221.TMP" +Creating command line "link.exe @C:\WINDOWS\TEMP\RSPE246.TMP" Compiling... MD2SKINI.cpp +MIDIIO.cpp +Object.cpp Linking... diff --git a/rawwaves/mditabl.h b/syntmono/miditabl.h similarity index 96% rename from rawwaves/mditabl.h rename to syntmono/miditabl.h index 01c9382..8938646 100644 --- a/rawwaves/mditabl.h +++ b/syntmono/miditabl.h @@ -1,17 +1,19 @@ -double __MIDI_To_Pitch[128] = { -8.18,8.66,9.18,9.72,10.30,10.91,11.56,12.25, -12.98,13.75,14.57,15.43,16.35,17.32,18.35,19.45, -20.60,21.83,23.12,24.50,25.96,27.50,29.14,30.87, -32.70,34.65,36.71,38.89,41.20,43.65,46.25,49.00, -51.91,55.00,58.27,61.74,65.41,69.30,73.42,77.78, -82.41,87.31,92.50,98.00,103.83,110.00,116.54,123.47, -130.81,138.59,146.83,155.56,164.81,174.61,185.00,196.00, -207.65,220.00,233.08,246.94,261.63,277.18,293.66,311.13, -329.63,349.23,369.99,392.00,415.30,440.00,466.16,493.88, -523.25,554.37,587.33,622.25,659.26,698.46,739.99,783.99, -830.61,880.00,932.33,987.77,1046.50,1108.73,1174.66,1244.51, -1318.51,1396.91,1479.98,1567.98,1661.22,1760.00,1864.66,1975.53, -2093.00,2217.46,2349.32,2489.02,2637.02,2793.83,2959.96,3135.96, -3322.44,3520.00,3729.31,3951.07,4186.01,4434.92,4698.64,4978.03, -5274.04,5587.65,5919.91,6271.93,6644.88,7040.00,7458.62,7902.13, -8372.02,8869.84,9397.27,9956.06,10548.08,11175.30,11839.82,12543.85,}; +#include "../STK/Object.h" + +double __MIDI_To_Pitch[128] = { +8.18,8.66,9.18,9.72,10.30,10.91,11.56,12.25, +12.98,13.75,14.57,15.43,16.35,17.32,18.35,19.45, +20.60,21.83,23.12,24.50,25.96,27.50,29.14,30.87, +32.70,34.65,36.71,38.89,41.20,43.65,46.25,49.00, +51.91,55.00,58.27,61.74,65.41,69.30,73.42,77.78, +82.41,87.31,92.50,98.00,103.83,110.00,116.54,123.47, +130.81,138.59,146.83,155.56,164.81,174.61,185.00,196.00, +207.65,220.00,233.08,246.94,261.63,277.18,293.66,311.13, +329.63,349.23,369.99,392.00,415.30,440.00,466.16,493.88, +523.25,554.37,587.33,622.25,659.26,698.46,739.99,783.99, +830.61,880.00,932.33,987.77,1046.50,1108.73,1174.66,1244.51, +1318.51,1396.91,1479.98,1567.98,1661.22,1760.00,1864.66,1975.53, +2093.00,2217.46,2349.32,2489.02,2637.02,2793.83,2959.96,3135.96, +3322.44,3520.00,3729.31,3951.07,4186.01,4434.92,4698.64,4978.03, +5274.04,5587.65,5919.91,6271.93,6644.88,7040.00,7458.62,7902.13, +8372.02,8869.84,9397.27,9956.06,10548.08,11175.30,11839.82,12543.85}; diff --git a/scores/bookert.ski b/syntmono/scores/bookert.ski similarity index 100% rename from scores/bookert.ski rename to syntmono/scores/bookert.ski diff --git a/scores/capture.ski b/syntmono/scores/capture.ski similarity index 100% rename from scores/capture.ski rename to syntmono/scores/capture.ski diff --git a/scores/chords.ski b/syntmono/scores/chords.ski similarity index 100% rename from scores/chords.ski rename to syntmono/scores/chords.ski diff --git a/scores/doogie.ski b/syntmono/scores/doogie.ski similarity index 100% rename from scores/doogie.ski rename to syntmono/scores/doogie.ski diff --git a/scores/drumfunk.ski b/syntmono/scores/drumfunk.ski similarity index 100% rename from scores/drumfunk.ski rename to syntmono/scores/drumfunk.ski diff --git a/scores/drumtest.ski b/syntmono/scores/drumtest.ski similarity index 100% rename from scores/drumtest.ski rename to syntmono/scores/drumtest.ski diff --git a/scores/duelingb.ski b/syntmono/scores/duelingb.ski similarity index 100% rename from scores/duelingb.ski rename to syntmono/scores/duelingb.ski diff --git a/scores/fiddle.ski b/syntmono/scores/fiddle.ski similarity index 100% rename from scores/fiddle.ski rename to syntmono/scores/fiddle.ski diff --git a/scores/flutbach.ski b/syntmono/scores/flutbach.ski similarity index 100% rename from scores/flutbach.ski rename to syntmono/scores/flutbach.ski diff --git a/scores/funicula.ski b/syntmono/scores/funicula.ski similarity index 100% rename from scores/funicula.ski rename to syntmono/scores/funicula.ski diff --git a/scores/funskini.ski b/syntmono/scores/funskini.ski similarity index 100% rename from scores/funskini.ski rename to syntmono/scores/funskini.ski diff --git a/scores/instructions b/syntmono/scores/instructions similarity index 57% rename from scores/instructions rename to syntmono/scores/instructions index 8a64d96..51f5c59 100644 --- a/scores/instructions +++ b/syntmono/scores/instructions @@ -1,11 +1,10 @@ -useage: testMono Instr file.ski - where Instr = Marimba Vibraphn AgogoBel Plucked - Mandolin Clarinet Flute Brass Bowed - Rhodey Wurley TubeBell HeavyMtl - PercFlut BeeThree Moog1 +Useage: syntmono Instr -r < scorefile + where Instr = one of these: +Clarinet Flute Brass Bowed Plucked Mandolin Marimba Vibraphn +AgogoBel Rhodey Wurley TubeBell HeavyMtl PercFlut BeeThree Moog1 +FMVoices VoicForm DrumSynt Shakers BowedBar - -Suggested Score Pairings: +Suggested Score/Instrument Pairings: BeeThree bookert.ski or organs.ski diff --git a/scores/lacrymos.ski b/syntmono/scores/lacrymos.ski similarity index 100% rename from scores/lacrymos.ski rename to syntmono/scores/lacrymos.ski diff --git a/scores/mandtune.ski b/syntmono/scores/mandtune.ski similarity index 100% rename from scores/mandtune.ski rename to syntmono/scores/mandtune.ski diff --git a/scores/marimba2.ski b/syntmono/scores/marimba2.ski similarity index 100% rename from scores/marimba2.ski rename to syntmono/scores/marimba2.ski diff --git a/scores/marimtst.ski b/syntmono/scores/marimtst.ski similarity index 100% rename from scores/marimtst.ski rename to syntmono/scores/marimtst.ski diff --git a/scores/misacrio.ski b/syntmono/scores/misacrio.ski similarity index 100% rename from scores/misacrio.ski rename to syntmono/scores/misacrio.ski diff --git a/scores/morazbel.ski b/syntmono/scores/morazbel.ski similarity index 100% rename from scores/morazbel.ski rename to syntmono/scores/morazbel.ski diff --git a/scores/organs.ski b/syntmono/scores/organs.ski similarity index 100% rename from scores/organs.ski rename to syntmono/scores/organs.ski diff --git a/scores/pickdamp.ski b/syntmono/scores/pickdamp.ski similarity index 100% rename from scores/pickdamp.ski rename to syntmono/scores/pickdamp.ski diff --git a/scores/pictures.ski b/syntmono/scores/pictures.ski similarity index 100% rename from scores/pictures.ski rename to syntmono/scores/pictures.ski diff --git a/scores/riderson.ski b/syntmono/scores/riderson.ski similarity index 100% rename from scores/riderson.ski rename to syntmono/scores/riderson.ski diff --git a/scores/scales.ski b/syntmono/scores/scales.ski similarity index 100% rename from scores/scales.ski rename to syntmono/scores/scales.ski diff --git a/scores/shaktest.ski b/syntmono/scores/shaktest.ski similarity index 100% rename from scores/shaktest.ski rename to syntmono/scores/shaktest.ski diff --git a/scores/simplgft.ski b/syntmono/scores/simplgft.ski similarity index 100% rename from scores/simplgft.ski rename to syntmono/scores/simplgft.ski diff --git a/scores/spain.ski b/syntmono/scores/spain.ski similarity index 100% rename from scores/spain.ski rename to syntmono/scores/spain.ski diff --git a/scores/spain0.ski b/syntmono/scores/spain0.ski similarity index 100% rename from scores/spain0.ski rename to syntmono/scores/spain0.ski diff --git a/scores/streetsf.ski b/syntmono/scores/streetsf.ski similarity index 100% rename from scores/streetsf.ski rename to syntmono/scores/streetsf.ski diff --git a/syntmono/scores/test.ski b/syntmono/scores/test.ski new file mode 100644 index 0000000..d2b00a3 --- /dev/null +++ b/syntmono/scores/test.ski @@ -0,0 +1,3 @@ +/* Howdy!! ToolKit96cpp SKINI File, Perry Cook */ +NoteOn 0.000000 1 60 127.000000 +NoteOff 0.3 1 60 63.500000 diff --git a/scores/thecars.ski b/syntmono/scores/thecars.ski similarity index 100% rename from scores/thecars.ski rename to syntmono/scores/thecars.ski diff --git a/scores/tubebell.ski b/syntmono/scores/tubebell.ski similarity index 100% rename from scores/tubebell.ski rename to syntmono/scores/tubebell.ski diff --git a/scores/vocaliz.ski b/syntmono/scores/vocaliz.ski similarity index 100% rename from scores/vocaliz.ski rename to syntmono/scores/vocaliz.ski diff --git a/syntmono/syntmono.cpp b/syntmono/syntmono.cpp new file mode 100644 index 0000000..62536e7 --- /dev/null +++ b/syntmono/syntmono.cpp @@ -0,0 +1,170 @@ +// syntmono.cpp +// +// An example STK program for monophonic voice playback and control. + +#include "../STK/WvOut.h" +#include "../STK/Instrmnt.h" +#include "../STK/Reverb.h" +#include "../STK/NRev.h" +#include "../STK/PRCRev.h" +#include "../STK/JCRev.h" +#include "../STK/SKINI11.h" +#include "../STK/SKINI11.msg" +#include "miditabl.h" + +int numStrings = 0; +int notDone = 1; +char **inputString; + +// Miscellaneous command-line parsing and instrument allocation +// functions are defined in utilites.cpp. +#include "utilities.h" + +// The input command pipe and socket threads are defined in threads.cpp. +#include "threads.h" + +Instrmnt *instrument; +WvOut **output; + +void main(int argc,char *argv[]) +{ + long i, j, synlength; + int type, rtInput = 0; + int numOuts = 0, outOne = 0; + int instNum; + char *fin; + MY_FLOAT reverbTime = 0.5; // in seconds + MY_FLOAT temp, byte3, lastPitch, outSample; + + // Check the command-line arguments for errors and to determine + // the number of WvOut objects to be instantiated. + numOuts = checkArgs(argc, argv); + output = (WvOut **) malloc(numOuts * sizeof(WvOut *)); + + // Instantiate the instrument from the command-line argument. + if ( (instNum=newInstByName(argv[1])) < 0 ) errorfun(argv[0]); + + // Parse the command-line flags and instantiate WvOut objects. + rtInput = parseArgs(argc, argv); + + SKINI11 *score = new SKINI11(); + Reverb *reverb = new PRCRev(reverbTime); + reverb->setEffectMix(0.2); + + + // If using realtime input, start the input thread. + if (rtInput) { +#if defined(__STK_REALTIME_) + if (rtInput == 2) startSocketThread(); + else startPipeThread(); + instrument->noteOn(200.0,0.1); +#endif + } + else { // Malloc inputString as 1 x STRING_LEN array + inputString = (char **) malloc(sizeof(char *)); + inputString[0] = (char *) malloc(STRING_LEN * sizeof(char)); + } + + // The runtime loop begins here: + notDone = 1; + synlength = RT_BUFFER_SIZE; + while(notDone || numStrings) { + if (rtInput) { + if (numStrings > 1) synlength = (long) RT_BUFFER_SIZE / numStrings; + else synlength = RT_BUFFER_SIZE; + for ( i=0; itick(instrument->tick()); + for ( j=0; jtick(outSample); + } + } + else { + fin = fgets(inputString[0],STRING_LEN,stdin); + if (fin == NULL) notDone = 0; + else { + numStrings++; + } + } + if (numStrings) { + score->parseThis(inputString[outOne]); + type = score->getType(); + if (type > 0) { + if (temp = score->getDelta()) { /* SKINI score file */ + synlength = (long) (temp * SRATE); + for ( i=0; itick(instrument->tick()); + for ( j=0; jtick(outSample); + } + synlength = 0; + } + if (type == __SK_NoteOn_ ) { + if (( byte3 = score->getByteThree() ) == 0) + instrument->noteOff(byte3*NORM_7); + else { + j = (int) score->getByteTwo(); + temp = __MIDI_To_Pitch[j]; + lastPitch = temp; + instrument->noteOn(temp,byte3*NORM_7); + } + } + else if (type == __SK_NoteOff_) { + byte3 = score->getByteThree(); + instrument->noteOff(byte3*NORM_7); + } + else if (type == __SK_ControlChange_) { + j = (int) score->getByteTwo(); + byte3 = score->getByteThree(); + instrument->controlChange(j,byte3); + } + else if (type == __SK_AfterTouch_) { + j = (int) score->getByteTwo(); + instrument->controlChange(128,j); + } + else if (type == __SK_PitchBend_) { + temp = score->getByteTwo(); + j = (int) temp; + temp -= j; + lastPitch = __MIDI_To_Pitch[j] * pow(2.0,temp / 12.0) ; + instrument->setFreq(lastPitch); + } + else if (type == __SK_ProgramChange_) { + j = (int) score->getByteTwo(); + if (j != instNum) { + instrument->noteOff(1.0); + for (i=0;i<4096;i++) { + outSample = reverb->tick(instrument->tick()); + for ( int k=0; ktick(outSample); + } + delete instrument; + if ( (instNum=newInstByNum(j)) < 0 ) { + // Default instrument = 0 + instNum = newInstByNum(0); + } + instrument->noteOn(lastPitch, 0.2); + } + } + } + if (rtInput) { + outOne += 1; + if (outOne == MAX_IN_STRINGS) outOne = 0; + } + numStrings--; + } + } + + for (i=0;itick(instrument->tick()); + for ( j=0; jtick(outSample); + } + + for ( i=0; i" with flags "" + +Creating temp file "C:\WINDOWS\TEMP\RSPC092.TMP" with contents +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSPC092.TMP" +Creating temp file "C:\WINDOWS\TEMP\RSPC093.TMP" with contents +Creating command line "link.exe @C:\WINDOWS\TEMP\RSPC093.TMP" +Compiling... +utilities.cpp +BowedBar.cpp +Linking... + + + +syntmono.exe - 0 error(s), 0 warning(s) diff --git a/syntmono/tcl/TCLBowedBar.tcl b/syntmono/tcl/TCLBowedBar.tcl new file mode 100644 index 0000000..9591b45 --- /dev/null +++ b/syntmono/tcl/TCLBowedBar.tcl @@ -0,0 +1,233 @@ +# Tcl/Tk Bowed Bar Model GUI for the Synthesis Toolkit (STK) + +set press 64.0 +set pitch 64.0 +set cont1 127.0 +set cont2 20.0 +set cont4 127.0 +set cont11 0.0 +set outID "stdout" +set commtype "stdout" +set struckbow 0 + +# Configure main window +wm title . "STK Bowed Bar Controller" +wm iconname . "bowedbar" +. config -bg black + +# Configure "communications" menu +menu .menu -tearoff 0 +menu .menu.communication -tearoff 0 +.menu add cascade -label "Communication" -menu .menu.communication \ + -underline 0 +.menu.communication add radio -label "Console" -variable commtype \ + -value "stdout" -command { setComm } +.menu.communication add radio -label "Socket" -variable commtype \ + -value "socket" -command { setComm } +. configure -menu .menu + +frame .leftsid -bg black + +proc myExit {} { + global pitch outID + puts $outID [format "NoteOff 0.0 1 %f 127" $pitch ] + flush $outID + puts $outID [format "ExitProgram"] + flush $outID + close $outID + exit +} + +proc noteOn {pitchVal pressVal} { + global outID + puts $outID [format "NoteOn 0.0 1 %f %f" $pitchVal $pressVal] + flush $outID +} + +proc noteOff {pitchVal pressVal} { + global outID + puts $outID [format "NoteOff 0.0 1 %f %f" $pitchVal $pressVal] + flush $outID +} + +proc patchChange {value} { + global outID press cont1 cont4 cont11 + set patch $value + puts $outID [format "ProgramChange 0.0 1 %i" $value] + flush $outID + set cont1 64.0 + set cont4 64.0 + set cont11 64.0 +} + +proc printWhatz {tag value1 value2 } { + global outID + puts $outID [format "%s %i %f" $tag $value1 $value2] + flush $outID +} + +proc changePress {value} { + global outID + puts $outID [format "AfterTouch 0.0 1 %f" $value] + flush $outID +} + +proc changePitch {value} { + global outID + puts $outID [format "PitchBend 0.0 1 %.3f" $value] + flush $outID +} + +proc activateVel {} { + global pitch + noteOn $pitch 127 + printWhatz "ControlChange 0.0 1 " 65 0 +} + +# Configure sliders +frame .press -bg grey66 + +button .press.pos -text Pos. -bg grey66 \ +-command {printWhatz "ControlChange 0.0 1 " 65 127} +button .press.vel -text Vel. -bg grey66 \ +-command activateVel + +scale .press.bPressure -from 0 -to 128 -length 200 \ +-command {changePress } -variable press\ +-orient horizontal -label "Pos on Bow / Vel of Bow" \ +-tickinterval 32 -showvalue true -bg grey66 + +scale .pitch -from 0 -to 128 -length 200 \ +-command {changePitch } -variable pitch \ +-orient horizontal -label "MIDI Note Number" \ +-tickinterval 32 -showvalue true -bg grey66 + +scale .cont2 -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 2} \ +-orient horizontal -label "Bowing Pressure" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont2 + +scale .cont4 -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 4} \ +-orient horizontal -label "Strike/Bow Position" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont4 + +scale .cont11 -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 11} \ +-orient horizontal -label "Integration" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont11 + +scale .reson -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 1} \ +-orient horizontal -label "Mode Resonance" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont1 + +. config -bg grey20 + +frame .bowstruk -bg black +radiobutton .bowstruk.bowed -text Bowed -bg grey66 \ +-command {printWhatz "ControlChange 0.0 1 " 64 127} \ +-value 1 -variable struckbow +radiobutton .bowstruk.struck -text Struck -bg grey66 \ +-command {printWhatz "ControlChange 0.0 1 " 64 0} \ +-value 0 -variable struckbow + +pack .press.pos -side left +pack .press.bPressure -side left +pack .press.vel -side left +pack .press -padx 10 -pady 10 +pack .pitch -padx 10 -pady 10 +pack .cont2 -padx 10 -pady 10 +pack .cont4 -padx 10 -pady 10 +pack .cont11 -padx 10 -pady 10 +pack .reson -padx 10 -pady 10 + +pack .bowstruk.bowed -side left -padx 5 +pack .bowstruk.struck -side left -padx 5 +pack .bowstruk -pady 10 + +frame .noteOn -bg black +button .noteOn.on -text NoteOn -bg grey66 -command { noteOn $pitch $press } +button .noteOn.off -text NoteOff -bg grey66 -command { noteOff $pitch 127.0 } +button .noteOn.exit -text "Exit Program" -bg grey66 -command myExit +pack .noteOn.on -side left -padx 5 +pack .noteOn.off -side left -padx 5 -pady 10 +pack .noteOn.exit -side left -padx 5 -pady 10 +pack .noteOn -pady 10 + +bind all { + noteOn $pitch $press +} + +# Bind an X windows "close" event with the Exit routine +bind . +myExit + +# Socket connection procedure +set d .socketdialog + +proc setComm {} { + global outID + global commtype + global d + if {$commtype == "stdout"} { + if { [string compare "stdout" $outID] } { + set i [tk_dialog .dialog "Break Socket Connection?" {You are about to break an existing socket connection ... is this what you want to do?} "" 0 Cancel OK] + switch $i { + 0 {set commtype "socket"} + 1 {close $outID + set outID "stdout"} + } + } + } elseif { ![string compare "stdout" $outID] } { + set sockport 2001 + set sockhost localhost + toplevel $d + wm title $d "STK Client Socket Connection" + wm resizable $d 0 0 + grab $d + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + -background white -font {Helvetica 10 bold} \ + -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} + frame $d.sockport + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ + -font {Helvetica 10 bold} + pack $d.message -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost + $d.sockport.entry insert 0 $sockport + frame $d.buttons + button $d.buttons.cancel -text "Cancel" -bg grey66 \ + -command { set commtype "stdout" + set outID "stdout" + destroy $d } + button $d.buttons.connect -text "Connect" -bg grey66 \ + -command { + set sockhost [$d.sockhost.entry get] + set sockport [$d.sockport.entry get] + set err [catch {socket $sockhost $sockport} outID] + + if {$err == 0} { + destroy $d + } else { + tk_dialog $d.error "Socket Error" {Error: Unable to make socket connection. Make sure the STK socket server is first running and that the port number is correct.} "" 0 OK + } } + pack $d.buttons.cancel -side left -padx 5 -pady 10 + pack $d.buttons.connect -side right -padx 5 -pady 10 + pack $d.buttons -side bottom -padx 5 -pady 10 + } +} + diff --git a/syntmono/tcl/TCLDemo.tcl b/syntmono/tcl/TCLDemo.tcl new file mode 100644 index 0000000..208df20 --- /dev/null +++ b/syntmono/tcl/TCLDemo.tcl @@ -0,0 +1,530 @@ +# Tcl/Tk Demo GUI for the Synthesis Toolkit (STK) +# by Gary P. Scavone, CCRMA, Stanford University, 1999. + +# Set initial control values +set pitch 64.0 +set press 64.0 +set velocity 96.0 +set cont1 0.0 +set cont2 10.0 +set cont4 20.0 +set cont11 64.0 +set outID "stdout" +set commtype "stdout" +set patchnum 0 +set oldpatch 0 + +# Configure main window +wm title . "STK Demo GUI" +wm iconname . "demo" +. config -bg black + +# Configure "communications" menu +menu .menu -tearoff 0 +menu .menu.communication -tearoff 0 +.menu add cascade -label "Communication" -menu .menu.communication \ + -underline 0 +.menu.communication add radio -label "Console" -variable commtype \ + -value "stdout" -command { setComm } +.menu.communication add radio -label "Socket" -variable commtype \ + -value "socket" -command { setComm } + +# Configure instrument change menu +menu .menu.instrument -tearoff 0 +.menu add cascade -label "Instrument" -menu .menu.instrument \ + -underline 0 +.menu.instrument add radio -label "Clarinet" -variable patchnum \ + -value 0 -command { patchChange $patchnum } +.menu.instrument add radio -label "Flute" -variable patchnum \ + -value 1 -command { patchChange $patchnum } +.menu.instrument add radio -label "Brass" -variable patchnum \ + -value 2 -command { patchChange $patchnum } +.menu.instrument add radio -label "Bowed String" -variable patchnum \ + -value 3 -command { patchChange $patchnum } +.menu.instrument add radio -label "Plucked String" -variable patchnum \ + -value 4 -command { patchChange $patchnum } +.menu.instrument add radio -label "Mandolin" -variable patchnum \ + -value 5 -command { patchChange $patchnum } +.menu.instrument add radio -label "Marimba" -variable patchnum \ + -value 6 -command { patchChange $patchnum } +.menu.instrument add radio -label "Vibraphone" -variable patchnum \ + -value 7 -command { patchChange $patchnum } +.menu.instrument add radio -label "Agogo Bell" -variable patchnum \ + -value 8 -command { patchChange $patchnum } +.menu.instrument add radio -label "Rhodey" -variable patchnum \ + -value 9 -command { patchChange $patchnum } +.menu.instrument add radio -label "Wurley" -variable patchnum \ + -value 10 -command { patchChange $patchnum } +.menu.instrument add radio -label "Tubular Bell" -variable patchnum \ + -value 11 -command { patchChange $patchnum } +.menu.instrument add radio -label "Heavy Metal" -variable patchnum \ + -value 12 -command { patchChange $patchnum } +.menu.instrument add radio -label "Percussive Flute" -variable patchnum \ + -value 13 -command { patchChange $patchnum } +.menu.instrument add radio -label "B3 Organ" -variable patchnum \ + -value 14 -command { patchChange $patchnum } +.menu.instrument add radio -label "Moog" -variable patchnum \ + -value 15 -command { patchChange $patchnum } +.menu.instrument add radio -label "FM Voices" -variable patchnum \ + -value 16 -command { patchChange $patchnum } +.menu.instrument add radio -label "Drum Kit" -variable patchnum \ + -value 18 -command { patchChange $patchnum } +.menu.instrument add radio -label "Maraca" -variable patchnum \ + -value 19 -command { patchChange $patchnum } +.menu.instrument add radio -label "Sekere" -variable patchnum \ + -value 20 -command { patchChange $patchnum } +.menu.instrument add radio -label "Cabasa" -variable patchnum \ + -value 21 -command { patchChange $patchnum } +.menu.instrument add radio -label "Bamboo" -variable patchnum \ + -value 22 -command { patchChange $patchnum } +.menu.instrument add radio -label "Waterdrop" -variable patchnum \ + -value 23 -command { patchChange $patchnum } +.menu.instrument add radio -label "Tambourine" -variable patchnum \ + -value 24 -command { patchChange $patchnum } +.menu.instrument add radio -label "Sleigh Bell" -variable patchnum \ + -value 25 -command { patchChange $patchnum } +.menu.instrument add radio -label "Guiro" -variable patchnum \ + -value 26 -command { patchChange $patchnum } +.menu.instrument add radio -label "Sticks" -variable patchnum \ + -value 27 -command { patchChange $patchnum } +.menu.instrument add radio -label "Crunch" -variable patchnum \ + -value 28 -command { patchChange $patchnum } +.menu.instrument add radio -label "Wrench" -variable patchnum \ + -value 29 -command { patchChange $patchnum } +.menu.instrument add radio -label "SandPaper" -variable patchnum \ + -value 30 -command { patchChange $patchnum } +.menu.instrument add radio -label "CokeCan" -variable patchnum \ + -value 31 -command { patchChange $patchnum } +. configure -menu .menu + +# Configure bitmap display +if {[file isdirectory bitmaps]} { + set bitmappath bitmaps +} else { + set bitmappath tcl/bitmaps +} +button .pretty -bitmap @$bitmappath/prcFunny.xbm \ + -background white -foreground black +.pretty config -bitmap @$bitmappath/prc.xbm +pack .pretty -padx 5 -pady 10 + +# Configure "note-on" buttons +frame .noteOn -bg black + +button .noteOn.on -text NoteOn -bg grey66 -command { noteOn $pitch $press } +button .noteOn.off -text NoteOff -bg grey66 -command { noteOff $pitch 127.0 } +button .noteOn.exit -text "Exit Program" -bg grey66 -command myExit +pack .noteOn.on -side left -padx 5 +pack .noteOn.off -side left -padx 5 -pady 10 +pack .noteOn.exit -side left -padx 5 -pady 10 + +pack .noteOn + +# Configure sliders +frame .left -bg black +frame .right -bg black + +scale .left.bPressure -from 0 -to 128 -length 200 \ +-command {changePress } -variable press \ +-orient horizontal -label "Breath Pressure" \ +-tickinterval 32 -showvalue true -bg grey66 + +scale .left.pitch -from 0 -to 128 -length 200 \ +-command {changePitch } -variable pitch \ +-orient horizontal -label "MIDI Note Number" \ +-tickinterval 32 -showvalue true -bg grey66 + +scale .left.cont2 -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 2} \ +-orient horizontal -label "Reed Stiffness" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont2 + +scale .right.cont4 -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 4} \ +-orient horizontal -label "Breath Noise" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont4 + +scale .right.cont11 -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 11} \ +-orient horizontal -label "Vibrato Rate" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont11 + +scale .right.cont1 -from 0 -to 128 -length 200 \ +-command {printWhatz "ControlChange 0.0 1 " 1} \ +-orient horizontal -label "Vibrato Amount" \ +-tickinterval 32 -showvalue true -bg grey66 \ +-variable cont1 + +pack .left.bPressure -padx 10 -pady 10 +pack .left.pitch -padx 10 -pady 10 +pack .left.cont2 -padx 10 -pady 10 +pack .right.cont4 -padx 10 -pady 10 +pack .right.cont11 -padx 10 -pady 10 +pack .right.cont1 -padx 10 -pady 10 + +pack .left -side left +pack .right -side right + +# DrumKit popup window +set p .drumwindow + +proc myExit {} { + global pitch outID + puts $outID [format "NoteOff 0.0 1 %3.2f 127" $pitch ] + flush $outID + puts $outID [format "ExitProgram"] + flush $outID + close $outID + exit +} + +proc noteOn {pitchVal pressVal} { + global outID + puts $outID [format "NoteOn 0.0 1 %3.2f %3.2f" $pitchVal $pressVal] + flush $outID +} + +proc noteOff {pitchVal pressVal} { + global outID + puts $outID [format "NoteOff 0.0 1 %3.2f %3.2f" $pitchVal $pressVal] + flush $outID +} + +proc patchChange {value} { + global outID bitmappath cont1 cont2 cont4 cont11 oldpatch press pitch + if {$value!=$oldpatch} { + if {$value < 19} { + puts $outID [format "ProgramChange 0.0 1 %2i" $value] + flush $outID + } + if {$value > 18 && $oldpatch < 19} { + puts $outID [format "ProgramChange 0.0 1 19"] + flush $outID + } + set oldpatch $value + # This stuff below sets up the correct bitmaps, slider labels, and control + # parameters. + if {$value==0} { # Clarinet + .pretty config -bitmap @$bitmappath/Klar.xbm + .left.bPressure config -state normal -label "Breath Pressure" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Reed Stiffness" + .right.cont4 config -state normal -label "Breath Noise" + .right.cont11 config -state normal -label "Vibrato Rate" + .right.cont1 config -state normal -label "Vibrato Amount" + set cont1 20.0 + set cont2 64.0 + set cont4 20.0 + set cont11 64.0 + printWhatz "ControlChange 0.0 1 " 1 $cont1 + printWhatz "ControlChange 0.0 1 " 2 $cont2 + printWhatz "ControlChange 0.0 1 " 4 $cont4 + printWhatz "ControlChange 0.0 1 " 11 $cont11 + } + if {$value==1} { # Flute + .pretty config -bitmap @$bitmappath/KFloot.xbm + .left.bPressure config -state normal -label "Breath Pressure" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Embouchure Adjustment" + .right.cont4 config -state normal -label "Breath Noise" + .right.cont11 config -state normal -label "Vibrato Rate" + .right.cont1 config -state normal -label "Vibrato Amount" + set cont1 20.0 + set cont2 64.0 + set cont4 20.0 + set cont11 64.0 + printWhatz "ControlChange 0.0 1 " 1 $cont1 + printWhatz "ControlChange 0.0 1 " 2 $cont2 + printWhatz "ControlChange 0.0 1 " 4 $cont4 + printWhatz "ControlChange 0.0 1 " 11 $cont11 + } + if {$value==2} { # Brass + .pretty config -bitmap @$bitmappath/KHose.xbm + .left.bPressure config -state normal -label "Breath Pressure" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Lip Adjustment" + .right.cont4 config -state normal -label "Slide Length" + .right.cont11 config -state normal -label "Vibrato Rate" + .right.cont1 config -state normal -label "Vibrato Amount" + set cont1 0.0 + set cont2 64.0 + set cont4 20.0 + set cont11 64.0 + printWhatz "ControlChange 0.0 1 " 1 $cont1 + printWhatz "ControlChange 0.0 1 " 2 $cont2 + printWhatz "ControlChange 0.0 1 " 4 $cont4 + printWhatz "ControlChange 0.0 1 " 11 $cont11 + } + if {$value==3} { # Bowed String + .pretty config -bitmap @$bitmappath/KFiddl.xbm + .left.bPressure config -state normal -label "Volume" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Bow Pressure" + .right.cont4 config -state normal -label "Bow Position" + .right.cont11 config -state normal -label "Vibrato Rate" + .right.cont1 config -state normal -label "Vibrato Amount" + set cont1 4.0 + set cont2 64.0 + set cont4 64.0 + set cont11 64.0 + printWhatz "ControlChange 0.0 1 " 1 $cont1 + printWhatz "ControlChange 0.0 1 " 2 $cont2 + printWhatz "ControlChange 0.0 1 " 4 $cont4 + printWhatz "ControlChange 0.0 1 " 11 $cont11 + } + if {$value==4} { # Yer Basic Pluck + .pretty config -bitmap @$bitmappath/KPluk.xbm + .left.bPressure config -state disabled -label "Disabled" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state disabled -label "Disabled" + .right.cont4 config -state disabled -label "Disabled" + .right.cont11 config -state disabled -label "Disabled" + .right.cont1 config -state disabled -label "Disabled" + } + if {$value==5} { # Mandolin + .pretty config -bitmap @$bitmappath/KPluk.xbm + .left.bPressure config -state disabled -label "Disabled" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Mandolin Body Size" + .right.cont4 config -state normal -label "Pick Position" + .right.cont11 config -state normal -label "String Sustain" + .right.cont1 config -state normal -label "String Detune" + set cont1 10.0 + set cont2 64.0 + set cont4 64.0 + set cont11 96.0 + printWhatz "ControlChange 0.0 1 " 1 $cont1 + printWhatz "ControlChange 0.0 1 " 2 $cont2 + printWhatz "ControlChange 0.0 1 " 4 $cont4 + printWhatz "ControlChange 0.0 1 " 11 $cont11 + } + if {$value>=6 && $value<=8 } { # Modal Instruments + .pretty config -bitmap @$bitmappath/KModal.xbm + .left.bPressure config -state normal -label "Volume" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -label "Stick Hardness" + .right.cont4 config -label "Stick Position" + .right.cont11 config -state normal -label "Vibrato Frequency" + .right.cont1 config -state normal -label "Vibrato Amount" + } + if {$value>=9 && $value <=14} { # FM Instruments + .pretty config -bitmap @$bitmappath/KFMod.xbm + .left.bPressure config -state normal -label "Modulator ADSR Amplitudes" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Modulator Index" + .right.cont4 config -state normal -label "FM Pair Crossfader" + .right.cont11 config -state normal -label "Vibrato Rate" + .right.cont1 config -state normal -label "Vibrato Amount" + } + if {$value==15} { # Moog + .pretty config -bitmap @$bitmappath/prcFunny.xbm + .left.bPressure config -state normal -label "Volume" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Filter Q" + .right.cont4 config -state normal -label "Filter Sweep Rate" + .right.cont11 config -state normal -label "Vibrato Rate" + .right.cont1 config -state normal -label "Vibrato Amount" + } + if {$value==16} { # FM Voices + .pretty config -bitmap @$bitmappath/KVoiceFM.xbm + .left.bPressure config -state normal -label "Loudness (Spectral Tilt)" + .left.pitch config -state normal -label "MIDI Note Number" + .left.cont2 config -state normal -label "Formant Q" + .right.cont4 config -state normal -label "Vowel (Bass, Tenor, Alto, Sop.)" + .right.cont11 config -state normal -label "Vibrato Rate" + .right.cont1 config -state normal -label "Vibrato Amount" + } + if {$value==18} { # Drum Kit + # Given the vastly different interface for the Drum Kit, we open + # a new GUI popup window with the appropriate controls and lock + # focus there until the user hits the "Close" button. We then + # switch back to the Clarinet (0) instrument. + global p + toplevel $p + wm title $p "STK DrumKit" + $p config -bg black + wm resizable $p 0 0 + grab $p + scale $p.velocity -from 0 -to 128 -length 100 \ + -variable velocity -orient horizontal -label "Velocity" \ + -tickinterval 64 -showvalue true -bg grey66 + pack $p.velocity -pady 5 -padx 5 + # Configure buttons + frame $p.buttons -bg black + frame $p.buttons.left -bg black + frame $p.buttons.right -bg black + + button $p.buttons.left.bass -text Bass -bg grey66 \ + -command { playDrum 36 } -width 7 + button $p.buttons.left.snare -text Snare -bg grey66 \ + -command { playDrum 38 } -width 7 + button $p.buttons.left.tomlo -text LoTom -bg grey66 \ + -command { playDrum 41 } -width 7 + button $p.buttons.left.tommid -text MidTom -bg grey66 \ + -command { playDrum 45 } -width 7 + button $p.buttons.left.tomhi -text HiTom -bg grey66 \ + -command { playDrum 50 } -width 7 + button $p.buttons.left.homer -text Homer -bg grey66 \ + -command { playDrum 90 } -width 7 + button $p.buttons.right.hat -text Hat -bg grey66 \ + -command { playDrum 42 } -width 7 + button $p.buttons.right.ride -text Ride -bg grey66 \ + -command { playDrum 46 } -width 7 + button $p.buttons.right.crash -text Crash -bg grey66 \ + -command { playDrum 49 } -width 7 + button $p.buttons.right.cowbel -text CowBel -bg grey66 \ + -command { playDrum 56 } -width 7 + button $p.buttons.right.tamb -text Tamb -bg grey66 \ + -command { playDrum 54 } -width 7 + button $p.buttons.right.homer -text Homer -bg grey66 \ + -command { playDrum 90 } -width 7 + + pack $p.buttons.left.bass -pady 5 + pack $p.buttons.left.snare -pady 5 + pack $p.buttons.left.tomlo -pady 5 + pack $p.buttons.left.tommid -pady 5 + pack $p.buttons.left.tomhi -pady 5 + pack $p.buttons.left.homer -pady 5 + pack $p.buttons.right.hat -pady 5 + pack $p.buttons.right.ride -pady 5 + pack $p.buttons.right.crash -pady 5 + pack $p.buttons.right.cowbel -pady 5 + pack $p.buttons.right.tamb -pady 5 + pack $p.buttons.right.homer -pady 5 + + pack $p.buttons.left -side left -pady 5 -padx 5 + pack $p.buttons.right -side right -pady 5 -padx 5 + pack $p.buttons -padx 5 -pady 10 + + button $p.close -text "Close" -bg grey66 \ + -command { destroy $p + set patchnum 0 + patchChange $patchnum} + pack $p.close -side bottom -padx 5 -pady 10 + } + if {$value>=19 && $value <=31} { # Shakers + .pretty config -bitmap @$bitmappath/phism.xbm + .left.bPressure config -state normal -label "Shake Energy" + .left.pitch config -state disabled -label "Disabled" + .left.cont2 config -state disabled -label "Disabled" + .right.cont4 config -state normal -label "(<--High) Damping (Low-->)" + .right.cont11 config -state normal -label "Number of Objects" + .right.cont1 config -state normal -label "Resonance Center Frequency" + if {$value==19} {set pitch 0} + if {$value==20} {set pitch 2} + if {$value==21} {set pitch 1} + if {$value==22} {set pitch 5} + if {$value==23} {set pitch 4} + if {$value==24} {set pitch 6} + if {$value==25} {set pitch 7} + if {$value==26} {set pitch 3} + if {$value==27} {set pitch 8} + if {$value==28} {set pitch 9} + if {$value==29} {set pitch 10} + if {$value==30} {set pitch 11} + if {$value==31} {set pitch 12} + puts $outID [format "NoteOn 0.0 1 %3.2f %3.2f" $pitch $press] + flush $outID + } + } +} + +#bind all { +bind . { + noteOn $pitch $press +} + +# Bind an X windows "close" event with the Exit routine +bind . +myExit + +proc playDrum {value} { + global velocity outID + puts $outID [format "NoteOn 0.0 1 %3i %3.2f" $value $velocity] + flush $outID +} + +proc printWhatz {tag value1 value2 } { + global outID + puts $outID [format "%s %2i %3.2f" $tag $value1 $value2] + flush $outID +} + +proc changePress {value} { + global outID + puts $outID [format "AfterTouch 0.0 1 %3.2f" $value] + flush $outID +} + +proc changePitch {value} { + global outID + puts $outID [format "PitchBend 0.0 1 %3.2f" $value] + flush $outID +} + +# Socket connection procedure +set d .socketdialog + +proc setComm {} { + global outID + global commtype + global d + if {$commtype == "stdout"} { + if { [string compare "stdout" $outID] } { + set i [tk_dialog .dialog "Break Socket Connection?" {You are about to break an existing socket connection ... is this what you want to do?} "" 0 Cancel OK] + switch $i { + 0 {set commtype "socket"} + 1 {close $outID + set outID "stdout"} + } + } + } elseif { ![string compare "stdout" $outID] } { + set sockport 2001 + set sockhost localhost + toplevel $d + wm title $d "STK Client Socket Connection" + wm resizable $d 0 0 + grab $d + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + -background white -font {Helvetica 10 bold} \ + -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} + frame $d.sockport + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ + -font {Helvetica 10 bold} + pack $d.message -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost + $d.sockport.entry insert 0 $sockport + frame $d.buttons + button $d.buttons.cancel -text "Cancel" -bg grey66 \ + -command { set commtype "stdout" + set outID "stdout" + destroy $d } + button $d.buttons.connect -text "Connect" -bg grey66 \ + -command { + set sockhost [$d.sockhost.entry get] + set sockport [$d.sockport.entry get] + set err [catch {socket $sockhost $sockport} outID] + + if {$err == 0} { + destroy $d + } else { + tk_dialog $d.error "Socket Error" {Error: Unable to make socket connection. Make sure the STK socket server is first running and that the port number is correct.} "" 0 OK + } } + pack $d.buttons.cancel -side left -padx 5 -pady 10 + pack $d.buttons.connect -side right -padx 5 -pady 10 + pack $d.buttons -side bottom -padx 5 -pady 10 + } +} diff --git a/TCLSpecs/TCLDrums.tcl b/syntmono/tcl/TCLDrums.tcl similarity index 79% rename from TCLSpecs/TCLDrums.tcl rename to syntmono/tcl/TCLDrums.tcl index 98a3214..e827bc9 100644 --- a/TCLSpecs/TCLDrums.tcl +++ b/syntmono/tcl/TCLDrums.tcl @@ -1,3 +1,6 @@ +# Tcl/Tk Drum GUI for the Synthesis Toolkit (STK) + +# Set initial control values set press 127 set outID "stdout" set commtype "stdout" @@ -77,6 +80,14 @@ pack .buttons -pady 5 -padx 5 button .exit -text "Exit Program" -bg grey66 -command myExit pack .exit -side bottom -pady 20 +#bind all { +bind . { + noteOn $pitch $press +} + +# Bind an X windows "close" event with the Exit routine +bind . +myExit + proc myExit {} { global outID puts $outID [format "ExitProgram"] @@ -115,21 +126,30 @@ proc setComm {} { } } elseif { ![string compare "stdout" $outID] } { set sockport 2001 + set sockhost localhost toplevel $d wm title $d "STK Client Socket Connection" wm resizable $d 0 0 grab $d - label $d.message -text "Specify a socket port number below (if different than the STK default of 2001) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ -background white -font {Helvetica 10 bold} \ -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} frame $d.sockport - entry $d.sockport.entry -width 6 - label $d.sockport.text -text "Socket Port Number:" \ + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ -font {Helvetica 10 bold} pack $d.message -side top -padx 5 -pady 10 - pack $d.sockport.text -side left -padx 1 -pady 10 - pack $d.sockport.entry -side right -padx 5 -pady 10 - pack $d.sockport -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost $d.sockport.entry insert 0 $sockport frame $d.buttons button $d.buttons.cancel -text "Cancel" -bg grey66 \ @@ -138,8 +158,10 @@ proc setComm {} { destroy $d } button $d.buttons.connect -text "Connect" -bg grey66 \ -command { + set sockhost [$d.sockhost.entry get] set sockport [$d.sockport.entry get] - set err [catch {socket localhost $sockport} outID] + set err [catch {socket $sockhost $sockport} outID] + if {$err == 0} { destroy $d } else { diff --git a/TCLSpecs/TCLPhys.tcl b/syntmono/tcl/TCLPhys.tcl similarity index 84% rename from TCLSpecs/TCLPhys.tcl rename to syntmono/tcl/TCLPhys.tcl index 64e0c06..826640b 100644 --- a/TCLSpecs/TCLPhys.tcl +++ b/syntmono/tcl/TCLPhys.tcl @@ -1,3 +1,6 @@ +# Tcl/Tk Physical Model GUI for the Synthesis Toolkit (STK) + +# Set initial control values set pitch 64.0 set press 64.0 set cont1 0.0 @@ -44,10 +47,10 @@ pack .instChoice.bowd -side left -padx 5 -pady 10 pack .instChoice -side top # Configure bitmap display -if {$tcl_platform(platform) == "windows"} { +if {[file isdirectory bitmaps]} { set bitmappath bitmaps } else { - set bitmappath TCLSpecs/bitmaps + set bitmappath tcl/bitmaps } button .pretty -bitmap @$bitmappath/prcFunny.xbm \ -background white -foreground black @@ -114,9 +117,11 @@ pack .right.vibrato -padx 10 -pady 10 pack .left -side left pack .right -side right +# Bind an X windows "close" event with the Exit routine +bind . +myExit + proc myExit {} { - global pitch - global outID + global pitch outID puts $outID [format "NoteOff 0.0 1 %f 127" $pitch ] flush $outID puts $outID [format "ExitProgram"] @@ -138,12 +143,7 @@ proc noteOff {pitchVal pressVal} { } proc patchChange {value} { - global outID - global bitmappath - global cont1 - global cont2 - global cont4 - global cont11 + global outID bitmappath cont1 cont2 cont4 cont11 puts $outID [format "ProgramChange 0.0 1 %i" $value] if {$value==0} { .pretty config -bitmap @$bitmappath/Klar.xbm @@ -200,21 +200,30 @@ proc setComm {} { } } elseif { ![string compare "stdout" $outID] } { set sockport 2001 + set sockhost localhost toplevel $d wm title $d "STK Client Socket Connection" wm resizable $d 0 0 grab $d - label $d.message -text "Specify a socket port number below (if different than the STK default of 2001) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ -background white -font {Helvetica 10 bold} \ -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} frame $d.sockport - entry $d.sockport.entry -width 6 - label $d.sockport.text -text "Socket Port Number:" \ + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ -font {Helvetica 10 bold} pack $d.message -side top -padx 5 -pady 10 - pack $d.sockport.text -side left -padx 1 -pady 10 - pack $d.sockport.entry -side right -padx 5 -pady 10 - pack $d.sockport -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost $d.sockport.entry insert 0 $sockport frame $d.buttons button $d.buttons.cancel -text "Cancel" -bg grey66 \ @@ -223,8 +232,10 @@ proc setComm {} { destroy $d } button $d.buttons.connect -text "Connect" -bg grey66 \ -command { + set sockhost [$d.sockhost.entry get] set sockport [$d.sockport.entry get] - set err [catch {socket localhost $sockport} outID] + set err [catch {socket $sockhost $sockport} outID] + if {$err == 0} { destroy $d } else { @@ -235,3 +246,4 @@ proc setComm {} { pack $d.buttons -side bottom -padx 5 -pady 10 } } + diff --git a/TCLSpecs/TCLShakers.tcl b/syntmono/tcl/TCLShakers.tcl similarity index 61% rename from TCLSpecs/TCLShakers.tcl rename to syntmono/tcl/TCLShakers.tcl index 6da7b68..89fb878 100644 --- a/TCLSpecs/TCLShakers.tcl +++ b/syntmono/tcl/TCLShakers.tcl @@ -1,10 +1,13 @@ +# Tcl/Tk Shakers GUI for the Synthesis Toolkit (STK) + +# Set initial control values set press 64.0 set cont1 64.0 set cont4 64.0 set cont11 64.0 set outID "stdout" set commtype "stdout" -set patchnum 19 +set patchnum 0 # Configure main window wm title . "STK Shakers Controller" @@ -59,29 +62,44 @@ pack .right -side right -padx 5 -pady 5 frame .left -bg black radiobutton .left.maraca -text Maraca -bg grey66 \ - -command { patchChange 19 } -variable patchnum -width 8 \ - -justify left -value 19 + -command { patchChange 0 } -variable patchnum -width 8 \ + -justify left -value 0 radiobutton .left.sekere -text Sekere -bg grey66 \ - -command { patchChange 20 } -variable patchnum -width 8 \ - -justify left -value 20 + -command { patchChange 2 } -variable patchnum -width 8 \ + -justify left -value 2 radiobutton .left.cabasa -text Cabasa -bg grey66 \ - -command { patchChange 21 } -variable patchnum -width 8 \ - -justify left -value 21 + -command { patchChange 1 } -variable patchnum -width 8 \ + -justify left -value 1 radiobutton .left.bamboo -text Bamboo -bg grey66 \ - -command { patchChange 22 } -variable patchnum -width 8 \ - -justify left -value 22 + -command { patchChange 5 } -variable patchnum -width 8 \ + -justify left -value 5 radiobutton .left.waterdrp -text Waterdrp -bg grey66 \ - -command { patchChange 23 } -variable patchnum -width 8 \ - -justify left -value 23 + -command { patchChange 4 } -variable patchnum -width 8 \ + -justify left -value 4 radiobutton .left.tambourn -text Tambourn -bg grey66 \ - -command { patchChange 24 } -variable patchnum -width 8 \ - -justify left -value 24 + -command { patchChange 6 } -variable patchnum -width 8 \ + -justify left -value 6 radiobutton .left.sleighbl -text Sleighbl -bg grey66 \ - -command { patchChange 25 } -variable patchnum -width 8 \ - -justify left -value 25 + -command { patchChange 7 } -variable patchnum -width 8 \ + -justify left -value 7 radiobutton .left.guiro -text Guiro -bg grey66 \ - -command { patchChange 26 } -variable patchnum -width 8 \ - -justify left -value 26 + -command { patchChange 3 } -variable patchnum -width 8 \ + -justify left -value 3 +radiobutton .left.stix1 -text Stix1 -bg grey66 \ + -command { patchChange 8 } -variable patchnum -width 8 \ + -justify left -value 8 +radiobutton .left.crunch1 -text Crunch1 -bg grey66 \ + -command { patchChange 9 } -variable patchnum -width 8 \ + -justify left -value 9 +radiobutton .left.wrench -text Wrench -bg grey66 \ + -command { patchChange 10 } -variable patchnum -width 8 \ + -justify left -value 10 +radiobutton .left.sandpapr -text SandPaper -bg grey66 \ + -command { patchChange 11 } -variable patchnum -width 8 \ + -justify left -value 11 +radiobutton .left.cokecan -text CokeCan -bg grey66 \ + -command { patchChange 12 } -variable patchnum -width 8 \ + -justify left -value 12 pack .left.maraca -pady 5 pack .left.sekere -pady 5 @@ -91,6 +109,11 @@ pack .left.waterdrp -pady 5 pack .left.tambourn -pady 5 pack .left.sleighbl -pady 5 pack .left.guiro -pady 5 +pack .left.stix1 -pady 5 +pack .left.crunch1 -pady 5 +pack .left.wrench -pady 5 +pack .left.sandpapr -pady 5 +pack .left.cokecan -pady 5 pack .left -side left -padx 10 @@ -98,6 +121,14 @@ pack .left -side left -padx 10 button .left.exit -text "Exit Program" -bg grey66 -command myExit pack .left.exit -pady 10 +#bind all { +bind . { + patchChange $patchnum +} + +# Bind an X windows "close" event with the Exit routine +bind . +myExit + proc myExit {} { global outID puts $outID [format "ExitProgram"] @@ -107,23 +138,14 @@ proc myExit {} { } proc patchChange {value} { - global outID - global press - global cont1 - global cont4 - global cont11 - puts $outID [format "ProgramChange 0.0 1 %i" $value] + global outID press + puts $outID [format "NoteOn 0.0 1 %i $press" $value] flush $outID - set cont1 64.0 - set cont4 64.0 - set cont11 64.0 } proc printWhatz {tag value1 value2 } { global outID - global press puts $outID [format "%s %i %f" $tag $value1 $value2] - puts $outID [format "AfterTouch 0.0 1 %f" $press] flush $outID } @@ -133,12 +155,6 @@ proc changePress {value} { flush $outID } -proc changePitch {value} { - global outID - puts $outID [format "PitchBend 0.0 1 %.3f" $value] - flush $outID -} - # Socket connection procedure set d .socketdialog @@ -157,21 +173,30 @@ proc setComm {} { } } elseif { ![string compare "stdout" $outID] } { set sockport 2001 + set sockhost localhost toplevel $d wm title $d "STK Client Socket Connection" wm resizable $d 0 0 grab $d - label $d.message -text "Specify a socket port number below (if different than the STK default of 2001) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ -background white -font {Helvetica 10 bold} \ -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} frame $d.sockport - entry $d.sockport.entry -width 6 - label $d.sockport.text -text "Socket Port Number:" \ + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ -font {Helvetica 10 bold} pack $d.message -side top -padx 5 -pady 10 - pack $d.sockport.text -side left -padx 1 -pady 10 - pack $d.sockport.entry -side right -padx 5 -pady 10 - pack $d.sockport -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost $d.sockport.entry insert 0 $sockport frame $d.buttons button $d.buttons.cancel -text "Cancel" -bg grey66 \ @@ -180,8 +205,10 @@ proc setComm {} { destroy $d } button $d.buttons.connect -text "Connect" -bg grey66 \ -command { + set sockhost [$d.sockhost.entry get] set sockport [$d.sockport.entry get] - set err [catch {socket localhost $sockport} outID] + set err [catch {socket $sockhost $sockport} outID] + if {$err == 0} { destroy $d } else { @@ -192,4 +219,3 @@ proc setComm {} { pack $d.buttons -side bottom -padx 5 -pady 10 } } - diff --git a/TCLSpecs/TCLStruk.tcl b/syntmono/tcl/TCLStruk.tcl similarity index 86% rename from TCLSpecs/TCLStruk.tcl rename to syntmono/tcl/TCLStruk.tcl index 4fbcdca..0e7f39d 100644 --- a/TCLSpecs/TCLStruk.tcl +++ b/syntmono/tcl/TCLStruk.tcl @@ -1,3 +1,6 @@ +# Tcl/Tk Plucked/Struck GUI for the Synthesis Toolkit (STK) + +# Set initial control values set pitch 64.0 set press 64.0 set cont1 0.0 @@ -59,10 +62,10 @@ pack .fm.tube -side left -padx 5 -pady 10 pack .fm # Configure bitmap display -if {$tcl_platform(platform) == "windows"} { +if {[file isdirectory bitmaps]} { set bitmappath bitmaps } else { - set bitmappath TCLSpecs/bitmaps + set bitmappath tcl/bitmaps } button .pretty -bitmap @$bitmappath/prcFunny.xbm \ -background white -foreground black @@ -129,10 +132,14 @@ pack .right.vibrato -padx 10 -pady 10 pack .left -side left pack .right -side right -bind all { +#bind all { +bind . { noteOn $pitch $press } +# Bind an X windows "close" event with the Exit routine +bind . +myExit + proc myExit {} { global pitch global outID @@ -231,21 +238,30 @@ proc setComm {} { } } elseif { ![string compare "stdout" $outID] } { set sockport 2001 + set sockhost localhost toplevel $d wm title $d "STK Client Socket Connection" wm resizable $d 0 0 grab $d - label $d.message -text "Specify a socket port number below (if different than the STK default of 2001) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ -background white -font {Helvetica 10 bold} \ -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} frame $d.sockport - entry $d.sockport.entry -width 6 - label $d.sockport.text -text "Socket Port Number:" \ + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ -font {Helvetica 10 bold} pack $d.message -side top -padx 5 -pady 10 - pack $d.sockport.text -side left -padx 1 -pady 10 - pack $d.sockport.entry -side right -padx 5 -pady 10 - pack $d.sockport -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost $d.sockport.entry insert 0 $sockport frame $d.buttons button $d.buttons.cancel -text "Cancel" -bg grey66 \ @@ -254,8 +270,10 @@ proc setComm {} { destroy $d } button $d.buttons.connect -text "Connect" -bg grey66 \ -command { + set sockhost [$d.sockhost.entry get] set sockport [$d.sockport.entry get] - set err [catch {socket localhost $sockport} outID] + set err [catch {socket $sockhost $sockport} outID] + if {$err == 0} { destroy $d } else { diff --git a/TCLSpecs/TCLVoice.tcl b/syntmono/tcl/TCLVoice.tcl similarity index 84% rename from TCLSpecs/TCLVoice.tcl rename to syntmono/tcl/TCLVoice.tcl index e20cc38..8460e42 100644 --- a/TCLSpecs/TCLVoice.tcl +++ b/syntmono/tcl/TCLVoice.tcl @@ -1,3 +1,6 @@ +# Tcl/Tk Voice GUI for the Synthesis Toolkit (STK) + +# Set initial control values set pitch 64.0 set press 64.0 set cont1 20.0 @@ -38,10 +41,10 @@ pack .instChoice.form -side left -padx 5 -pady 10 pack .instChoice -side top # Configure bitmap display -if {$tcl_platform(platform) == "windows"} { +if {[file isdirectory bitmaps]} { set bitmappath bitmaps } else { - set bitmappath TCLSpecs/bitmaps + set bitmappath tcl/bitmaps } button .pretty -bitmap @$bitmappath/prcFunny.xbm \ -background white -foreground black @@ -108,6 +111,9 @@ pack .right.vibrato -padx 10 -pady 10 pack .left -side left pack .right -side right +# Bind an X windows "close" event with the Exit routine +bind . +myExit + proc myExit {} { global pitch global outID @@ -188,21 +194,30 @@ proc setComm {} { } } elseif { ![string compare "stdout" $outID] } { set sockport 2001 + set sockhost localhost toplevel $d wm title $d "STK Client Socket Connection" wm resizable $d 0 0 grab $d - label $d.message -text "Specify a socket port number below (if different than the STK default of 2001) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ + label $d.message -text "Specify a socket host and port number below (if different than the STK defaults shown) and then click the \"Connect\" button to invoke a socket-client connection attempt to the STK socket server." \ -background white -font {Helvetica 10 bold} \ -wraplength 3i -justify left + frame $d.sockhost + entry $d.sockhost.entry -width 15 + label $d.sockhost.text -text "Socket Host:" \ + -font {Helvetica 10 bold} frame $d.sockport - entry $d.sockport.entry -width 6 - label $d.sockport.text -text "Socket Port Number:" \ + entry $d.sockport.entry -width 15 + label $d.sockport.text -text "Socket Port:" \ -font {Helvetica 10 bold} pack $d.message -side top -padx 5 -pady 10 - pack $d.sockport.text -side left -padx 1 -pady 10 - pack $d.sockport.entry -side right -padx 5 -pady 10 - pack $d.sockport -side top -padx 5 -pady 10 + pack $d.sockhost.text -side left -padx 1 -pady 2 + pack $d.sockhost.entry -side right -padx 5 -pady 2 + pack $d.sockhost -side top -padx 5 -pady 2 + pack $d.sockport.text -side left -padx 1 -pady 2 + pack $d.sockport.entry -side right -padx 5 -pady 2 + pack $d.sockport -side top -padx 5 -pady 2 + $d.sockhost.entry insert 0 $sockhost $d.sockport.entry insert 0 $sockport frame $d.buttons button $d.buttons.cancel -text "Cancel" -bg grey66 \ @@ -211,8 +226,10 @@ proc setComm {} { destroy $d } button $d.buttons.connect -text "Connect" -bg grey66 \ -command { + set sockhost [$d.sockhost.entry get] set sockport [$d.sockport.entry get] - set err [catch {socket localhost $sockport} outID] + set err [catch {socket $sockhost $sockport} outID] + if {$err == 0} { destroy $d } else { diff --git a/TCLSpecs/bitmaps/KFMod.xbm b/syntmono/tcl/bitmaps/KFMod.xbm similarity index 100% rename from TCLSpecs/bitmaps/KFMod.xbm rename to syntmono/tcl/bitmaps/KFMod.xbm diff --git a/TCLSpecs/bitmaps/KFiddl.xbm b/syntmono/tcl/bitmaps/KFiddl.xbm similarity index 100% rename from TCLSpecs/bitmaps/KFiddl.xbm rename to syntmono/tcl/bitmaps/KFiddl.xbm diff --git a/TCLSpecs/bitmaps/KFloot.xbm b/syntmono/tcl/bitmaps/KFloot.xbm similarity index 100% rename from TCLSpecs/bitmaps/KFloot.xbm rename to syntmono/tcl/bitmaps/KFloot.xbm diff --git a/TCLSpecs/bitmaps/KHose.xbm b/syntmono/tcl/bitmaps/KHose.xbm similarity index 100% rename from TCLSpecs/bitmaps/KHose.xbm rename to syntmono/tcl/bitmaps/KHose.xbm diff --git a/TCLSpecs/bitmaps/KModal.xbm b/syntmono/tcl/bitmaps/KModal.xbm similarity index 100% rename from TCLSpecs/bitmaps/KModal.xbm rename to syntmono/tcl/bitmaps/KModal.xbm diff --git a/TCLSpecs/bitmaps/KPluk.xbm b/syntmono/tcl/bitmaps/KPluk.xbm similarity index 100% rename from TCLSpecs/bitmaps/KPluk.xbm rename to syntmono/tcl/bitmaps/KPluk.xbm diff --git a/TCLSpecs/bitmaps/KVoicForm.xbm b/syntmono/tcl/bitmaps/KVoicForm.xbm similarity index 100% rename from TCLSpecs/bitmaps/KVoicForm.xbm rename to syntmono/tcl/bitmaps/KVoicForm.xbm diff --git a/TCLSpecs/bitmaps/KVoiceFM.xbm b/syntmono/tcl/bitmaps/KVoiceFM.xbm similarity index 100% rename from TCLSpecs/bitmaps/KVoiceFM.xbm rename to syntmono/tcl/bitmaps/KVoiceFM.xbm diff --git a/TCLSpecs/bitmaps/Klar.xbm b/syntmono/tcl/bitmaps/Klar.xbm similarity index 100% rename from TCLSpecs/bitmaps/Klar.xbm rename to syntmono/tcl/bitmaps/Klar.xbm diff --git a/TCLSpecs/bitmaps/kasm.xbm b/syntmono/tcl/bitmaps/kasm.xbm similarity index 100% rename from TCLSpecs/bitmaps/kasm.xbm rename to syntmono/tcl/bitmaps/kasm.xbm diff --git a/syntmono/tcl/bitmaps/phism.xbm b/syntmono/tcl/bitmaps/phism.xbm new file mode 100644 index 0000000..2d4268f --- /dev/null +++ b/syntmono/tcl/bitmaps/phism.xbm @@ -0,0 +1,90 @@ +#define phism_width 100 +#define phism_height 100 +static char phism_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x20,0x84,0x10,0x42,0x08,0x21, + 0x84,0x10,0x42,0x08,0x21,0x84,0xf0,0x48,0x29,0xa5,0x94,0x52,0x4a,0x29,0xa5, + 0x94,0x52,0x4a,0x51,0xf2,0x24,0x84,0x10,0x42,0x08,0x21,0x84,0x10,0x42,0x08, + 0x21,0x8a,0xf6,0x90,0x52,0x4a,0x29,0xa5,0x94,0x52,0x4a,0x29,0xa5,0x14,0x21, + 0xf6,0xc4,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0xe8, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0xc0,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xf7,0xd4,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xf7,0xc8,0xab,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xfa,0xd5,0x55,0x55,0xbb,0xf7,0xe0,0x57,0x55,0x55,0x55,0x55,0x55,0xf5,0xea, + 0xaa,0xaa,0xca,0xf7,0xc8,0x57,0x55,0x55,0xfd,0xff,0x57,0xd5,0xd7,0xaa,0xaa, + 0xaa,0xf7,0xd0,0xab,0xaa,0xaa,0xfa,0xff,0xab,0xea,0xeb,0x55,0x55,0xd5,0xf7, + 0xc8,0x57,0x55,0xd5,0xff,0xff,0xbf,0xaa,0xd7,0xaa,0xaa,0xaa,0xf7,0xe4,0xab, + 0xaa,0xaa,0xff,0xff,0x7f,0x55,0xeb,0x55,0x55,0xd5,0xf7,0xc8,0x57,0x55,0xf5, + 0x7f,0x80,0xff,0xaa,0xab,0xaa,0xaa,0xaa,0xf7,0xd0,0xab,0xaa,0xea,0x7f,0x80, + 0xff,0x55,0x57,0x55,0x55,0xd5,0xf7,0xc4,0x57,0x55,0xfd,0x07,0x00,0xfc,0xab, + 0xab,0xaa,0xaa,0xaa,0xf7,0xe8,0xab,0xaa,0xfa,0x07,0x00,0xfc,0x57,0x57,0x55, + 0x55,0xd5,0xf7,0xc0,0x57,0x55,0x7f,0xe0,0x81,0xc1,0xaf,0xaa,0xaa,0xaa,0xaa, + 0xf7,0xd4,0xab,0xaa,0x7e,0xe0,0x81,0xc1,0x5f,0x55,0x55,0x55,0xd5,0xf7,0xc8, + 0xaf,0xaa,0x1f,0xe0,0x81,0x03,0xbf,0xaa,0xaa,0xaa,0xaa,0xf7,0xe0,0x53,0x55, + 0x1f,0xe0,0x81,0x03,0x7f,0x55,0x55,0x55,0xd5,0xf7,0xc8,0xaf,0xaa,0xe7,0x01, + 0x84,0x03,0xbf,0xaa,0xb6,0xaa,0xaa,0xf7,0xd0,0x57,0x55,0xe7,0x01,0x80,0x03, + 0x7f,0x55,0xa9,0x55,0xd5,0xf7,0xc8,0xab,0xaa,0xe7,0x09,0x80,0x03,0xbf,0xaa, + 0xaa,0xaa,0xaa,0xf7,0xe4,0xd7,0xea,0xe7,0x21,0x00,0xc0,0x7f,0x55,0x55,0x55, + 0xd5,0xf7,0xc8,0x57,0xd5,0xe7,0x01,0x10,0xc0,0xbf,0xaa,0xaa,0xaa,0xaa,0xf7, + 0xd0,0xab,0xea,0x07,0x00,0x00,0xc2,0x7f,0x55,0x55,0x55,0xd5,0xf7,0xc4,0x57, + 0xd5,0x07,0x00,0x00,0xc0,0xbf,0xad,0xaa,0xaa,0xaa,0xf7,0xd0,0xab,0xea,0x07, + 0x80,0x06,0x0c,0x7c,0x55,0x55,0x55,0xd5,0xf7,0xc8,0xab,0xea,0x47,0x00,0x06, + 0x0c,0xbc,0xaa,0xaa,0xaa,0xaa,0xf7,0xe4,0x57,0xd5,0x87,0x81,0x07,0x0e,0xfc, + 0xd5,0xaa,0x56,0xd5,0xf7,0xd0,0xab,0xea,0x87,0x83,0x27,0x0e,0xfc,0xaa,0xaa, + 0xaa,0xaa,0xf7,0xc4,0xaf,0xea,0x87,0x87,0x07,0x02,0xfc,0x55,0x55,0x55,0xd5, + 0xf7,0xd0,0x53,0xd5,0x87,0x87,0x07,0x0a,0xfc,0xaa,0xaa,0xaa,0xaa,0xf7,0xc8, + 0xaf,0xea,0x87,0x07,0x00,0x00,0xfc,0x57,0x55,0x55,0xd5,0xf7,0xe4,0xab,0xea, + 0x87,0x17,0x00,0x00,0xfc,0xab,0xaa,0xaa,0xaa,0xf7,0xd0,0xab,0xaa,0x1f,0x00, + 0x42,0x3c,0xff,0x7f,0x55,0x55,0xd5,0xf7,0xc4,0xaf,0xaa,0x1f,0x00,0x00,0x3c, + 0xff,0xbf,0xaa,0xaa,0xaa,0xf7,0xd0,0xab,0xaa,0x1f,0x00,0x00,0xfc,0xff,0xff, + 0x57,0x55,0xd5,0xf7,0xc8,0x57,0x55,0x1f,0x00,0x00,0xfc,0xff,0xff,0xab,0xaa, + 0xaa,0xf7,0xe4,0xab,0xaa,0x7f,0x10,0x1e,0xc0,0x3f,0xfc,0x5f,0x55,0xd5,0xf7, + 0xd0,0xab,0xaa,0xfe,0x40,0x1e,0xc1,0x3f,0xfc,0xbf,0xaa,0xaa,0xf7,0xc4,0xaf, + 0xab,0xfe,0x07,0x1e,0xfc,0xff,0xc3,0xff,0x57,0xd5,0xf7,0xd0,0x53,0xaf,0xfa, + 0x07,0x1e,0xfc,0xff,0xc3,0xff,0xa9,0xaa,0xf7,0xc8,0xaf,0x5f,0xf5,0x7f,0x80, + 0xff,0xfe,0x3f,0xfc,0x5f,0xd5,0xf7,0xe4,0x53,0xaf,0xea,0x7f,0x88,0xff,0xfd, + 0x3f,0xfc,0xbf,0xaa,0xf7,0xd0,0x5f,0xd5,0xd5,0xff,0xff,0xbf,0xea,0xff,0xe0, + 0x7f,0xd5,0xf7,0xc4,0xa3,0xea,0xaa,0xff,0xff,0x7f,0xd5,0xff,0xe0,0xff,0xaa, + 0xf7,0xd0,0x5f,0xd5,0xd5,0xff,0xff,0xbf,0xea,0xff,0xe0,0x7f,0xd5,0xf7,0xc8, + 0xa3,0xea,0x56,0xfd,0xff,0xaf,0xaa,0xfe,0x07,0xfe,0xaa,0xf7,0xe4,0x5f,0xd5, + 0x55,0xfd,0xff,0x53,0x55,0xfd,0x07,0x7e,0xd5,0xf7,0xd0,0xa7,0xea,0xaf,0x6a, + 0x6b,0xad,0xaa,0xfa,0x7f,0xf8,0xaa,0xf7,0xc4,0x5b,0xd5,0x57,0x55,0x55,0x55, + 0x55,0xf5,0x7f,0x78,0xd5,0xf7,0xd0,0x57,0x55,0x55,0x55,0x55,0x55,0x55,0xb5, + 0xff,0xff,0xaa,0xf7,0xc8,0xab,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xff,0x7f, + 0xd5,0xf7,0xe4,0x57,0x55,0xff,0x55,0x55,0x55,0x55,0x55,0xfd,0xdf,0xaa,0xf7, + 0xc8,0xab,0xaa,0xfe,0x55,0x55,0x55,0x55,0x55,0xfd,0x5f,0xd5,0xf7,0xd0,0xab, + 0xaa,0xaa,0x56,0x55,0x55,0x55,0x55,0xd5,0xb7,0xaa,0xf7,0xc4,0x57,0x55,0x55, + 0x55,0x55,0x55,0x55,0x55,0xd5,0x57,0xd5,0xf7,0xe8,0x57,0x55,0x55,0x55,0x55, + 0x55,0x55,0x55,0x55,0xad,0xaa,0xf7,0xc0,0xab,0x55,0x55,0x55,0x55,0x55,0x55, + 0x55,0x55,0x55,0xd5,0xf7,0xd4,0xab,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xaa,0xf7,0xc8,0x57,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0xd5, + 0xf7,0xe0,0xab,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xf7,0xc8, + 0x57,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0xd5,0xf7,0xd0,0xab,0xaa, + 0x7f,0x5d,0x7d,0xbf,0xfa,0xeb,0xf5,0xaa,0xaa,0xf7,0xc8,0x57,0xad,0xff,0xba, + 0x5a,0x7f,0xf5,0xd7,0x6a,0x55,0xd5,0xf7,0xe4,0x57,0x55,0xd5,0x5d,0xbd,0xae, + 0xae,0xee,0xff,0xaa,0xaa,0xf7,0xc8,0xab,0xaa,0xab,0xbd,0x5a,0xbd,0x5e,0xdd, + 0x7b,0x55,0xd5,0xf7,0xd0,0xab,0xaa,0xd5,0x5b,0xbd,0x4e,0xad,0xea,0xf6,0xaa, + 0xaa,0xf7,0xc8,0x57,0x55,0xd7,0xbd,0x5a,0xbd,0x5e,0xd5,0x6d,0x55,0xd5,0xf7, + 0xe0,0x57,0x55,0x7f,0xfb,0xbf,0xae,0xfa,0xeb,0xf6,0xaa,0xaa,0xf7,0xc8,0xab, + 0xaa,0x7f,0xfd,0x5f,0x5d,0xf5,0xd7,0x69,0x55,0xd5,0xf7,0xd4,0x57,0x55,0xff, + 0xfa,0xbf,0xae,0xfa,0xeb,0xf6,0xaa,0xaa,0xf7,0xc0,0xab,0xaa,0x55,0x5d,0x5d, + 0xad,0xaa,0xee,0x6a,0x55,0xd5,0xf7,0xe8,0x57,0x55,0xab,0xba,0xba,0xbe,0xaa, + 0xde,0xf5,0xaa,0xaa,0xf7,0xc4,0xab,0xaa,0x55,0x5d,0x5d,0x4d,0x5d,0xed,0x75, + 0x55,0xd5,0xf7,0xd0,0x57,0x55,0xab,0xba,0xba,0xbe,0xae,0xde,0xea,0xaa,0xaa, + 0xf7,0xc8,0xab,0xaa,0x55,0x5d,0x5d,0xbf,0xfa,0xeb,0xf5,0x56,0xd5,0xf7,0xe4, + 0x57,0x55,0xab,0xba,0xba,0x7e,0xf5,0xd7,0xea,0xaa,0xaa,0xf7,0xd0,0xab,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0x2a,0x55,0xd5,0xf7,0xc8,0xd7,0xaa,0xaa,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xf7,0xe0,0x57,0x55,0x55,0x55,0x55,0x55, + 0x55,0x55,0x55,0x55,0xd5,0xf7,0xc8,0xab,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xaa,0xaa,0xf7,0xd0,0x57,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, + 0xd5,0xf7,0xc8,0xab,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xf7, + 0xe4,0x57,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0xd5,0xf7,0xc8,0xab, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xf7,0xd0,0x55,0x55,0x55, + 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0xd5,0xf7,0xc4,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xf7,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xf7,0xc0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xf7,0xf8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xf7,0xf4,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0}; diff --git a/TCLSpecs/bitmaps/prc.xbm b/syntmono/tcl/bitmaps/prc.xbm similarity index 100% rename from TCLSpecs/bitmaps/prc.xbm rename to syntmono/tcl/bitmaps/prc.xbm diff --git a/TCLSpecs/bitmaps/prcFunny.xbm b/syntmono/tcl/bitmaps/prcFunny.xbm similarity index 100% rename from TCLSpecs/bitmaps/prcFunny.xbm rename to syntmono/tcl/bitmaps/prcFunny.xbm diff --git a/syntmono/threads.cpp b/syntmono/threads.cpp new file mode 100644 index 0000000..7e7d640 --- /dev/null +++ b/syntmono/threads.cpp @@ -0,0 +1,282 @@ +// Thread functions for use with syntmono. +// +// Gary P. Scavone, 1999. + +#include "threads.h" + +#if defined(__STK_REALTIME_) + +#define SERVICE_PORT 2001 // Socket Port ID number + +// Do OS dependent declarations and includes +#if defined(__OS_IRIX_) +#include +#include +#include +#include +#include +#include +#include + +pid_t string_thread; + +#elif defined(__OS_Linux_) +#include +#include +#include +#include +#include +#include + +pthread_t string_thread; + +#elif defined(__OS_Win_) +#include +#include + +unsigned long string_thread; + +#endif + + +// The thread function definition protocols are slightly +// different under Irix, Linux, and Windoze. +#if defined(__OS_IRIX_) + +void newStringByPipe(void *) + +#elif defined(__OS_Linux_) + +void *newStringByPipe(void *) + +#elif defined(__OS_Win_) + +void newStringByPipe(void *) + +#endif + +{ + extern int numStrings, notDone; + extern char **inputString; + int i; + + // Malloc inputString. + inputString = (char **) malloc(MAX_IN_STRINGS * sizeof(char *)); + for ( i=0;i MAX_IN_STRINGS) { + fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); + numStrings--; + } + inOne++; + if (inOne == MAX_IN_STRINGS) inOne = 0; + } + } + + // Free inputString. + for ( i=0;i maxfd) maxfd = accept_id; + FD_CLR(soc_id, &rmask); + } + for (fd=0;fd<=maxfd;fd++) { // look for other sockets with data + if (FD_ISSET(fd, &rmask)) { // process the data + parsing = 1; + while (parsing) { + i = recv(fd, socBuf, STRING_LEN,0); + if (i==0) { + printf("Closing a socket connection.\n"); + FD_CLR(fd, &mask); +#if defined(__OS_Win_) + closesocket(fd); +#else + close(fd); +#endif + parsing = 0; + } + n = 0; + while (n < i) { + inputString[inOne][m++] = socBuf[n]; + if (socBuf[n++] == '\n') { + if (inputString[inOne][2] == 'i' && inputString[inOne][3] == 't' + && inputString[inOne][1] == 'x' && inputString[inOne][0] == 'E') { + notDone = 0; + n = i; + parsing = 0; + } + else { + m = 0; + if (n >= i) parsing = 0; + numStrings++; + if (numStrings > MAX_IN_STRINGS) { + fprintf(stderr,"Losing MIDI data ... try increasing MAX_IN_STRINGS.\n"); + numStrings--; + } + inOne++; + if (inOne == MAX_IN_STRINGS) inOne = 0; + memset(inputString[inOne], 0, STRING_LEN); + } + } + } + } + } + } + } +#if defined(__OS_Win_) // Stupid Windoze only stuff + closesocket(soc_id); + WSACleanup(); +#else + shutdown(soc_id,0); +#endif + + // Free inputString. + for ( i=0;i for .snd output file,\n"); + printf(" -w for .wav output file,\n"); + printf(" -m for .mat output file,\n"); +#if defined(__STK_REALTIME_) + printf(" -r for realtime output,\n"); + printf(" -ip for realtime input by pipe (versus scorefile),\n"); + printf(" (won't work under Win95/98),\n"); + printf(" -is for realtime input by socket (versus scorefile),\n"); +#endif + printf(" and Instr = one of these:\n"); + for (i=0;i are not specified,\n"); + printf("default names will be indicated. Each flag must\n"); + printf("include its own '-' sign.\n\n"); + exit(0); +} + +int checkArgs(int numArgs, char *args[]) +{ + int w, i = 2, j = 0; + int numOutputs = 0; + char flags[16] = ""; + + if (numArgs < 3 || numArgs > 10) errorfun(args[0]); + + while (i < numArgs) { + if (args[i][0] == '-') { + if ( (args[i][1] == 'r') || (args[i][1] == 's') || + (args[i][1] == 'w') || (args[i][1] == 'm') ) + numOutputs++; + else if (args[i][1] == 'i') { + if ( (args[i][2] != 's') && (args[i][2] != 'p') ) errorfun(args[0]); + } + else errorfun(args[0]); + flags[j] = args[i][1]; + j++; + } + i++; + } + + /* Check for multiple flags of the same type */ + for (i=0; i<=j; i++) { + w = i+1; + while (w <= j) { + if (flags[i] == flags[w]) { + printf("\nError: Multiple command line flags of the same type specified.\n\n"); + errorfun(args[0]); + } + w++; + } + } + + /* Make sure we have at least one output type */ + if (numOutputs < 1) errorfun(args[0]); + + return numOutputs; +} + +int parseArgs(int numArgs, char *args[]) +{ + int i = 2, j = 0; + int realtime = 0; + char fileName[256]; + extern WvOut **output; + + while (i < numArgs) { + if (args[i][0] == '-') { + switch(args[i][1]) { + + case 'i': +#if defined(__STK_REALTIME_) + if (args[i][2] == 's') realtime = 2; + else realtime = 1; + break; +#else + errorfun(args[0]); +#endif + + case 'r': +#if defined(__STK_REALTIME_) + output[j] = new RTWvOut(SRATE,1); + j++; + break; +#else + errorfun(args[0]); +#endif + + case 'w': + if ((i+1 < numArgs) && args[i+1][0] != '-') { + i++; + strcpy(fileName,args[i]); + } + else strcpy(fileName,"testwav"); + output[j] = new WavWvOut(1,fileName); + j++; + break; + + case 's': + if ((i+1 < numArgs) && args[i+1][0] != '-') { + i++; + strcpy(fileName,args[i]); + } + else strcpy(fileName,"testsnd"); + output[j] = new SndWvOut(1,fileName); + j++; + break; + + case 'm': + if ((i+1 < numArgs) && args[i+1][0] != '-') { + i++; + strcpy(fileName,args[i]); + } + else strcpy(fileName,"testmat"); + output[j] = new MatWvOut(1,fileName); + j++; + break; + + default: + errorfun(args[0]); + break; + } + } + i++; + } + return realtime; +} diff --git a/syntmono/utilities.h b/syntmono/utilities.h new file mode 100644 index 0000000..45bf16c --- /dev/null +++ b/syntmono/utilities.h @@ -0,0 +1,15 @@ +// Miscellaneous parsing and error functions for use with syntmono. +// +// Gary P. Scavone, 1999. + +#include "../STK/Instrmnt.h" + +int newInstByNum(int instNum); + +int newInstByName(char *instName); + +void errorfun(char *func); + +int checkArgs(int numArgs, char *args[]); + +int parseArgs(int numArgs, char *args[]);