squinewave — A mostly bandlimited shape-shifting square-pulse-saw-sinewave oscillator with hardsync.
This oscillator generates a variable shape waveform that can morph freely between classical shapes sine, square, pulse and saw. The shape is controlled by two interacting values: clip (squareness) and "skew" (symmetry). All shapes use a minimum number of samples per transition (ie, the sharp end of a saw or a pulse uses minimum N samples), this makes output bandlimited. At higher frequency, the minimum sweep rate takes over, so over a certain pitch all shapes "degrade" to sinewave. The minimum sweep rate is i-time configurable. Hardsync (a very quick sweep to phase=0) is supported, and a sync signal is output once per cycle.
aout [, asyncout] squinewave acps, aClip, aSkew, asyncin [, iMinSweep] [, iphase]
aout [, asyncout] squinewave acps, aClip, aSkew [, ksyncin] [, iMinSweep] [, iphase]
iMinSweep (optional) -- Range 4 and up. Sample count of the shortest square/pulse edges. Default: sr/3000 (mostly alias-free).
iphase (optional, default=-1) -- initial phase, range 0-2, proportional to the segments of the waveform (see notes). If negative value is given, skip setting phase
aout -- audio output, normalized +/-1
asyncout -- (optional) - Sync signal: 1 at endpoint of each cycle, else 0.
acps -- frequency. Range 0-anything; negative freq not implemented.
aClip -- "squareness" of waveform shape. Range 0-1. Clip 0 is sinewave (or saw), clip 1 is squarewave (or pulse).
aSkew -- symmetry of waveform shape. Range -1 to +1. Skew = 0 is symmetric like sine or square. Skew +1 or -1 is right/left-facing saw or pulse.
asyncin/ksyncin -- (optional, ignored if not a-rate) - when >= 1, waveform quickly sweeps to phase 0. Sweep length is 0 to about 1.5*iMinSweep samples depending on current phase.
The squinewave opcode is a variable shape oscillator with internally generated waveform. The waveform has two parts:
cosine sweep down, followed by flat part at -1
cosine sweep up, followed by flat part at +1
At the end of (2), sync signal is output.
Clip (0-1) | controls the proportion of flatness to sweep length in each segment. |
Skew (-1 to +1) | controls the proportions of segments (1) and (2) in the waveform cycle. |
If skew < 0, part (1) is shorter, if skew > 0, (1) is longer than (2)
Classic waveforms have simple values:
sine: clip=0, skew=0
saw(like): clip=0, skew=+1 or -1 (left- or right-facing)
square: clip=1, skew=0
pulse: clip=1, skew=+1 or -1
Fractional values generate intermediate waveforms.
asyncinHardsync input (asyncin >= 1) makes the waveform quickly sweep to end by raising frequency to 2 * sr/iMinSweep. Sync pulses are thus sharper than the pulse waveform.
iMinSweepThe waveform is bandlimited by always using a minimum number of samples for cosine sweeps, even when clip/skew are at extreme values. This is controlled by iMinSweep. Since iMinSweep is counted in samples, the waveform output is dependent on samplerate, but the spectrum will be very similiar independent of sr. Default sr/3000 is fairly "soft", the actual values are 14 samples at 44.1K, 16 at 48K, 32 at 96K etc. iMinSweep is counted in integers, although not strictly necessary.
If several units of squinewave are run in unison, it is recommend to use different iMinSweep values. The min sweep value creates "dips" or quieter areas in the overtone series. By using slightly different min sweep settings, spectrum is filled, rather than emphasizing the spectral profile.
squinewave is based on cosine instead of sine to generate the waveform. (This simplifies the controlling logic.) The difference is that cosine cos(0) = 1, whereas sin(0) = 0. This means that hardsync occurs when waveform is at peak, just about to enter its down sweep. (Sinewave hardsync would occur at a zero-crossing)
TipThe length of hardsync sweeps allows chain syncing several squinewave units, creating staggered hardsync pulses.
Pitch stabilityNote that iMinSweep and shape limits the ability of squinewave to match frequency exactly. When skew or clip is active, and FM is applied, the squinewave waveform will be longer or shorter than the exact period. The differences even out however, so with symmetric FM, squinewave drifts back to match average frequency. Sinewave unshaped signal (clip=skew=0) matches poscil output to 7-8 significant digits (also under FM).
Initial phaseSetting initial phase is useful if squinewave is used as a shaped LFO. Init phase is split into 4 segments with symbolic values range 0-2, so it will start at the expected place regardless of skew/clip values. 0-1 represents the first part, 1-2 the second. Some interesting iphase segment values are:
0 - start of 1st sweep down.
0.5 - end of down sweep (start of "low" flat section).
1 - midpoint, end of 1st flat section, start 2nd, "up" sweep
1.5 - end of up sweep, (start of "high" flat section)
0.25 and 1.25 are 0-crossings at middle of down/up sweep sections.
0.75 and 1.75 are middle of low/high flat sections.
If iphase < 0 (skip) at first use, initial phase is set to 1.25, ie 0-crossing of the "up" sweep. This makes output look like a sinewave.
Here is an example of the squinewave opcode. Play squinewave.csd
Example 1003. Example of the squinewave opcode.
See the sections Real-time Audio and Command Line Flags for more information on using command line flags.
<CsoundSynthesizer> <CsOptions> ; Select audio/midi flags here according to platform -odac ;;;realtime audio out ;-iadc ;;;uncomment -iadc if realtime audio input is needed too ; For Non-realtime ouput leave only the line below: ; -o sqrt.wav -W ;;; for file output any platform </CsOptions> <CsInstruments> ;################################################# 0dbfs = 1.1 nchnls = 2 ksmps = 100 ; aSyncin, FMod for instr 2 gafmod init 0 gasync init 0 ; Modulator squinewave instr 1 ; freq start, end acps line p4, p3, p5 ; shape start, end aclip line p6, p3, p7 askew line p8, p3, p9 ; ar, async squinewave aFreq , aclip, askew [, asyncin, iMinSweep, iphase] aout1, gasync squinewave cpsoct(acps), aclip, askew, 0, 17 outs1 aout1 gaFMod = aout1 endin ; squinewave using gaFMod and gasync input from i1 instr 2 ; freq & shape start, end acps line p4, p3, p5 aclip line p6, p3, p7 askew line p8, p3, p9 aFMindex line p10, p3, p11 asyncin = gasync * p12 afreq = cpsoct(acps + aFMindex * gaFMod) ; ar squinewave aFreq, aclip, askew [, asyncin, iMinSweep, iphase] aout2 squinewave afreq , aclip, askew, asyncin outs2 aout2 endin </CsInstruments> <CsScore> ; First part instr 1 hardsyncs instr 2 (p12) ; p4=fund clip skew i1 0 1. 6.11 6.06 0 1 -1 +1 i1 + 1. pp5 2.03 pp7 0 pp9 0 i1 + 1. pp5 7.11 pp7 .8 pp9 -.8 i1 + 2. pp5 8.11 pp7 .2 pp9 1 i1 + .5 pp5 6.05 pp7 .5 pp9 -.6 i1 + 2.5 pp5 6.05 pp7 1 pp9 1 ; p4=fund clip skew p10=FM p12=sync i2 0 .5 1.08 2.06 0 .3 -.5 +.5 0 0 1 i2 + .5 pp5 4.03 pp7 .5 pp9 -.6 pp11 . . i2 + .5 pp5 5.11 pp7 1 pp9 .5 pp11 . . i2 + .5 pp5 6.01 pp7 .8 pp9 -.5 pp11 . . i2 + .5 pp5 2.11 pp7 .1 pp9 .3 pp11 . . i2 + .5 pp5 3.11 pp7 .8 pp9 -.8 pp11 . . i2 + 2. pp5 4.00 pp7 0 pp9 0 pp11 . . i2 + 3. pp5 3.00 pp7 .3 pp9 1 pp11 . . s ; End section, reset clock ; Second part instr 1 outputs FM for instr 2 (p10, p11) ; p4=fund clip skew i1 0 1. 6.11 6.06 0 1 -.3 +.3 i1 + 1. pp5 2.03 pp7 0 pp9 0 i1 + 1. pp5 7.11 pp7 .8 pp9 .8 i1 + 2. pp5 8.11 pp7 0 pp9 .4 i1 + .5 pp5 6.05 pp7 .5 pp9 -.6 i1 + 2.5 pp5 6.05 pp7 .4 pp9 .8 ; p4=fund clip skew p10=FM p12=sync i2 0 .5 8.08 6.06 0 .3 -.5 +.5 0 3 0 i2 + .5 pp5 6.03 pp7 .5 pp9 -.6 pp11 2 . i2 + .5 pp5 5.11 pp7 1 pp9 .5 pp11 < . i2 + .5 pp5 6.01 pp7 .8 pp9 -.5 pp11 1 . i2 + .5 pp5 5.11 pp7 .5 pp9 .3 pp11 < . i2 + .5 pp5 9.04 pp7 .1 pp9 -.3 pp11 .5 . i2 + 2. pp5 8.11 pp7 .4 pp9 .4 pp11 2 . i2 + 3. pp5 8.11 pp7 0 pp9 0 pp11 3 . e </CsScore> </CsoundSynthesizer>