Csound Csound-dev Csound-tekno Search About

Re: Pulse width modulation!

Date1998-12-14 09:46
FromPaul Winkler
SubjectRe: Pulse width modulation!
Hans Mikelson wrote:
> I'm planning on implementing this as a vco opcode so if someone comes up
> with something better than what I have now I'll use that instead if they
> don't mind.


Well, I have not yet tried your version, so I'll leave it up to you to
decide whether my approach is better or worse! Feel free to use my code
(or anything vaguely based on it) if you like.

So, here's a version of pulse-width modulation I'm pretty happy
with. It sounds pretty nice to my ear, and it allows for much less
aliasing than my first attempt. The aliasing can be reduced as much as
you want, at the expense of some brightness. And this doesn't
require sr = kr, so it works nicely in realtime
situations.

Incidentally, this should also work as a strategy for
reduced-aliasing simulated square waves... just leave out the offset!

Thanks to the folks who chimed in... I love seeing these radically
different approaches to the same problem.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;copyright 1998 Paul M. Winkler, zarmzarm@erols.com
;****++++
;**** Last modified: Mon Dec 14 03:52:13 1998
;****----
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


	sr	=	44100
 	kr	=	2205
	ksmps	=	20
	nchnls	=	1

instr 5
; another try by PW
; We simulate a square wave by overdriving a table index, and simulate
; pwm by varying an offset to the index.
; Aliasing is reduced by making the amount of overdrive be 
; inversely proportional to pitch.

  ipitch    =    cpspch( p4)
  ianti_alias     = 10          ; recommended range is 4 to about 30
  isquareness     =    sr/(ipitch * ianti_alias) 
; Infinite isquareness yields a square wave. isquareness < 1 
; yields a sine.
  knarrowness     =    gkLFO *isquareness
  aindex    oscil      isquareness, ipitch, 1    ;  sine wave index.
  aindex    =          aindex + knarrowness
  aout      table      aindex, 5, 1, 0 ; f5 should be straight line +- 1
  aout      dcblock     aout
  kamp      adsr        .01, .02, .5, .2
  kamp      =           kamp*32000*gkamp
            out         aout*kamp
endin

instr 98                        ; global fades
gkamp expseg 0.005, p4, 1, (p3 - (p4 * 2)), 1, p4, 0.005
; p4 is rise AND decay time
print gkamp
endin

instr 99                        ; global LFO
klfo linseg p4, p3, p5          ; ramp from p4 to p5
gkLFO oscil 0.5, klfo, 1         ; use f1
gkLFO = gkLFO + 0.5          ; range from 0 to 1
endin

;;;;;;;;;;;;;;;;;;;;; END OF ORC ;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;; BEGIN SCO ;;;;;;;;;;;;;;;;;;;;;;;;;;;

t 0 120

; LFO wave shape ... it's weighted toward the top.
; I should figure out how to make the modulation respond
; better to a linear control signal! 
f1 0 512 7    1 128 .5 128 -1 128 .5 128 1

; straight line for instr 5
f5 0 512 -7   -1 512 1 

; Set up LFO and fades.
i98 0 32 1 
i99 0 32  .3 .6 

; bass_1, track 0, now at 0
i 5 0 0.5 6.00 
i 5 0.5 0.5 6.01 
i 5 1 0.5 6.02 
i 5 1.5 0.5 7.00 
i 5 2 0.5 6.00 
i 5 2.5 0.5 7.00 
i 5 3 0.5 6.01 
i 5 3.5 0.5 6.02 
i 5 4 0.5 6.05 
i 5 4.5 0.5 6.00 
i 5 5 0.5 5.09 
i 5 5.5 0.5 6.02 
i 5 6 0.5 6.05 
i 5 6.5 0.5 6.04 
i 5 7 0.5 6.03 
i 5 7.5 0.5 6.00 

; bass_1, track 0, now at 8
i 5 8 0.5 8 
i 5 8.5 0.5 8.01 
i 5 9 0.5 8.02 
i 5 9.5 0.5 9 
i 5 10 0.5 8 
i 5 10.5 0.5 9 
i 5 11 0.5 8.01 
i 5 11.5 0.5 8.02 
i 5 12 0.5 8.05 
i 5 12.5 0.5 8 
i 5 13 0.5 7.09 
i 5 13.5 0.5 8.02 
i 5 14 0.5 8.05 
i 5 14.5 0.5 8.04 
i 5 15 0.5 8.03 
i 5 15.5 0.5 8 

; bass_1, track 0, now at 16
i 5 16 0.5 5 
i 5 16.5 0.5 5.01 
i 5 17 0.5 5.02 
i 5 17.5 0.5 6 
i 5 18 0.5 5 
i 5 18.5 0.5 6 
i 5 19 0.5 5.01 
i 5 19.5 0.5 5.02 
i 5 20 0.5 5.05 
i 5 20.5 0.5 5 
i 5 21 0.5 4.09 
i 5 21.5 0.5 5.02 
i 5 22 0.5 5.05 
i 5 22.5 0.5 5.04 
i 5 23 0.5 5.03 
i 5 23.5 0.5 5 

; bass_1, track 0, now at 24
i 5 24 0.5 11 
i 5 24.5 0.5 11.01 
i 5 25 0.5 11.02 
i 5 25.5 0.5 12 
i 5 26 0.5 11 
i 5 26.5 0.5 12 
i 5 27 0.5 11.01 
i 5 27.5 0.5 11.02 
i 5 28 0.5 11.05 
i 5 28.5 0.5 11 
i 5 29 0.5 10.09 
i 5 29.5 0.5 11.02 
i 5 30 0.5 11.05 
i 5 30.5 0.5 11.04 
i 5 31 0.5 11.03 
i 5 31.5 0.5 11 

e

;;;;;;;;;;;;;;; END SCO ;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;; Just for the heck of it, here's the Pscore perl
script
;;;;;;;;;;;;;;;;;;;; that generated the score. Note that I took out
;;;;;;;;;;;;;;;;;;;; "use strict" from Pscore.pm to get this to work.

#!/usr/bin/perl

$orc = "/home/pw/Orcs+Scores/PW/pwm.orc";
$sco = "/home/pw/Orcs+Scores/PW/playing1.sco";
$output = "inst5_test.wav";

open(OUTFILE, ">$sco") || die "Can't open output score: $!"; 
select OUTFILE;

use Csound::Pscore;

# Let's make a pattern of 16th notes with these pitches.
@pch = qw(6.00 6.01 6.02  7.00
	  6.00 7.00 6.01  6.02
	  6.05 6.00 5.09  6.02
	  6.05 6.04 6.03  6.00
	  );
for ($i = 0; $i < 16; ++$i) {
    @Current[$i] = [ i, 2, $i * .5, .5, $pch[$i] ];
# Can't make the list with qw() or $i doesn't get interpolated!
}

store_chunk("bass_1");

print("t 0 120\n");

comment("sine for instr 1");
# print("f1 0 16384 10 1 \n");
print("f1 0 512 7    1 128 .5 128 -1 128 .5 128 1 ");

# comment("pseudo-square wave, not normalized, for instr 2");
# print("f2 0  512 -7  20000 250 20000 0 -20000 262 -20000 \n");
# comment("triangle wave for instr 3");
# print("f3 0 512 7   0 128 1 256 -1 128 0 \n"); 

comment("straight line for instr 5");
print("f5 0 512 -7   -1 512 1 \n");  


$start = 0;
for ($i = 5; $i <= 5; ++$i) { # cycle through some instruments.
                              # TEMPORARILY SET TO ONLY USE i5 !!
    load_chunk( "bass_1");
    pfields(1, "= $i"); # Select an instrument

    $Now[$Track] = $start;

    comment("Set up LFO and fades.");
    $dur = 32 ;
    print("i98 $start $dur 2 \n");
    print("i99 $start $dur  .3 .6 \n");
    
    # Here's where stuff actually happens.
    play;
    pfields(4, "+=2"); # change octave
    play;
    pfields(4, "-=3");
    play;
    pfields(4, "+=6");
    play;
    $start = $Now[$Track]
}

# PERF TIME!
close OUTFILE;