Monday, May 19, 2014

23. Phase Modulation Example


A Phase Modulation example is presented.




Instead of using the function that was named add() in our csound module, it will be renamed as addTag(), to make its meaning clear. Further, there are checks to see if the first and last character are the angle brackets.


# moduleCsound.py

from os import system
from time import sleep

L = []

startSyn,stopSyn='<CsoundSynthesizer>','</CsoundSynthesizer>'
startOpt,stopOpt='<CsOptions>','</CsOptions>'
startIns,stopIns='<CsInstruments>','</CsInstruments>'
startSco,stopSco='<CsScore>','</CsScore>'

def addTag(*b):
    """
    Adding Tag terms to csd file
    """
    for s in b:
        if s[0]!='<' or s[-1]!='>':
            raise Exception("AddTag only adds Tags")
    L.extend(b)
    return

def rem(remark):
    """
    Generates one line of comment
    """
    L.append('; %s' % remark)

def header(sr=44100,ksmps=10,nch=1,amp=1):
    """
    Generates 4 headers in the orchesta section
    """
    L.append('sr = %d' % sr)
    L.append('ksmps = %d' % ksmps)
    L.append('nchnls = %d' % nch)
    L.append('0dbfs = %d' % amp)
    return

def instrument(rem,instr,S):
    """
    Generates instrument code given a String
    in the orchestra section
    """
    tL = S.split('\n')
    if tL[0]!='' or tL[-1]!='' : raise Exception("Instr Str")
    tL = [' '*2 + tl for tl in tL]
    tL[0] = 'instr %d' % instr # (3)
    tL[-1] = 'endin'
    if rem!='': L.append('; ' + rem)
    L.extend(tL)
    return

def table(rem,tabNum,tabTime,tabSize,tabGEN,*tab):
    """
    Generates f line inside score section
    """
    tup = (tabNum,tabTime,tabSize,tabGEN)
    String = 'f %s %s %s %s' % tup
    LString1=[' ' + str(i) for i in tab]
    String1 = "".join(LString1)
    if rem!='': L.append('; ' + rem)
    L.append(String+String1)
    return

def score(instr,start,dur,*p):
    """
    Generates i line inside score section
    """
    String = 'i %s %s %s' % (instr,start,dur)
    LString1=[' ' + str(pf) for pf in p]
    String1 = "".join(LString1)
    L.append(String+String1)
    return
    
def writeRun(fname):
    """
    Write out the csd file and perform it
    """
    OutL=[Lu + '\n' for Lu in L]
    out = open('%s.csd' % fname,'w')
    out.writelines(OutL)
    out.close()
    sleep(1)
    cmd = 'csound -o %s.wav %s.csd' % (fname,fname)
    status=system(cmd)
    if status!=0: raise Exception ('Could not write wav file')
    return



This equation shows what we are trying to do. In the csound program we have to prefix each variable with the letter a to indicate that it is an audio rate variable. First y3 is found as the output of an oscil with a frequency of f3. Then y2p is found, as a sum of phasor, of frequency f2, and the signal y3. The variable y2p, is a phase term. Using the table opcode, y2p, is decoded, to y2. Finally yp is found, which is the sum of a phasor of frequency f1, and the signal y2. Finally we decode the yp with the table opcode to get the y output.




These steps are indicated in this Instrument String Template. There are 3 placeholders representing f3, f2 and f1. We have fixed the values of A, and B. After getting y, two envelopes are created for the left and right channels. There is a fast attack, and then the signal slowly decays, with fade-out, at the end.




The ten instruments are created such that f3 equals 5 Hz, f2 equals 20 Hz, and f1 is going from 2200 Hz to 1300 Hz in steps of minus 100 Hz. Python knows we have a multi-line statement if there is a starting delimiter such as square brackets, until it finds a matching closing delimiter. The indentation level of a multi-line statement is that of the line that started the delimiter.




Now the sine table is created and 10 scores are written, one for each instrument.


# PhaseEx.py

from moduleCsound import *
addTag(startSyn,startOpt,stopOpt,startIns)
header(nch=2)

# *** Instrument String Template ***
PhaseExTemplate="""
ay3 oscil .5, %s, 1 ; f3 (B=.5)
ay2p phasor %s ; f2
ay2p = ay2p + ay3
ay2 table ay2p, 1, 1, 0, 1
ay2 = ay2*2 ; A=2
ayp phasor %s; f1
ayp = ayp + ay2
ay table ayp, 1, 1, 0, 1
aenvL linseg 0,.01,.5,.19,.2,p3-.3,.2,.1,0
aenvR linseg 0,.01,.6,.19,.2,p3-.3,.2,.1,0
aL = ay*aenvL
aR = ay*aenvR
out aL, aR
"""

N = 10

PhaseExStr=[
  PhaseExTemplate % (5,20,2200-100*i)
    for i in range(N)]
for i in range(N):
  instrument('HaHa',
             i+1,
             PhaseExStr[i])

addTag(stopIns,startSco)

# *** Table ***
table('1. Sine wave',1,0,8192,10,1)

# *** Score ***
rem('Phase Mod Score')
for i in range(N): score(i+1,0.4*i,0.4)

addTag(stopSco,stopSyn)
writeRun('Tut23')



The wav file shows the effect of the envelope.




This is the frequency spectrum of the first score using the first instrument.




You will find additional information at pythonaudio.blogspot.com, including the source code.



This is the video of Tutorial 23:



22. Phase Modulation


Phase Modulation is very similar to Frequency Modulation.




We will use a sine table created using GEN10. Also, we will use a normalized x, such that 0 points to the first table entry and 1 points to the last table entry. The middle entry corresponds to 0.5.




The first instrument string creates a 1 Hz ramp, which is stored in a phasing variable. It acts as an index, to a sine table so the sine wave is generated in the audio output. Note, we have to set the mode to 1 so it knows that x=0 refers to the first entry in the table, and x=1 refers to the last entry of the table. Note we can also get the cosine, if the offset is 0.25 and wrap-around mode is used, so numbers like 1.25 map back to 0.25.


# Cos1.py

from moduleCsound import *
add(startSyn,startOpt,stopOpt,startIns)
header(nch=2)

# *** Instrument Strings ***
SinStr="""
aphas phasor 1
aout table aphas, 1, 1
out aphas,aout
"""

CosStr="""
aphas phasor 1
aout table aphas, 1,1,0.25,1
out aphas,aout
"""

instrument('sin',1,SinStr)
instrument('cos',2,CosStr)

add(stopIns,startSco)

# *** Table ***
table('1. Sine wave',1,0,8192,10,1)

# *** Score ***
rem('Sine wave')
score(1,0,1)
rem('Cosine wave')
score(2,1,1)

add(stopSco,stopSyn)
writeRun('Tut22a')



For the first second, we get the sine wave. In the next second, we have the cosine wave.




This results in a similar sine and cosine wave. Now the phasor term is added a value of 0.25.


# Cos2.py

from moduleCsound import *
add(startSyn,startOpt,stopOpt,startIns)
header(nch=2)

# *** Instrument Strings ***
SinStr="""
aphas phasor 1
aout table aphas, 1, 1
out aphas,aout
"""

CosStr="""
aphas phasor 1
aphas = aphas + 0.25
aout table aphas, 1,1,0,1
out aphas,aout
"""

instrument('sin',1,SinStr)
instrument('cos',2,CosStr)

add(stopIns,startSco)

# *** Table ***
table('1. Sine wave',1,0,8192,10,1)

# *** Score ***
rem('Sine wave')
score(1,0,1)
rem('Cosine wave')
score(2,1,1)

add(stopSco,stopSyn)
writeRun('Tut22b')



You can see that the phasor for the cosine portion goes from 0.25 to 1.25. It is clipped off at 1 for the audio as the maximum amp has been defined as 1.




Now the phase input is doubled, so the output signal is twice the frequency. Note by having different kinds of phases, we can have almost any kind of output. In phase modulation, we have to use the table opcode but the input phase could be any function.


# Sin2.py

from moduleCsound import *
add(startSyn,startOpt,stopOpt,startIns)
header(nch=2)

# *** Instrument Strings ***
SinStr="""
aphas phasor 1
aout table aphas, 1, 1
out aphas,aout
"""

Sin2Str="""
aphas phasor 1
aphas = aphas*2
aout table aphas, 1,1,0,1
out aphas,aout
"""

instrument('sin',1,SinStr)
instrument('sin2',2,Sin2Str)

add(stopIns,startSco)

# *** Table ***
table('1. Sine wave',1,0,8192,10,1)

# *** Score ***
rem('Sine wave')
score(1,0,1)
rem('Cosine wave')
score(2,1,1)

add(stopSco,stopSyn)
writeRun('Tut22c')



This shows that we do indeed get a sine wave with twice the frequency. Note, the input phase is clipped off at 1.5 sec, in the wave output. Internally, it works as expected in generating the cosine.




This shows a more complicated example. Here we have an Instrument String where the table is reading a phase from a sine wave, leading to a FM-like output.


# SinSin.py

from moduleCsound import *
add(startSyn,startOpt,stopOpt,startIns)
header(nch=2)

# *** Instrument Strings ***
SinStr="""
aphas phasor 1
aout table aphas, 1, 1
out aphas,aout
"""

SinSinStr="""
ain oscil 0.5, 1, 1
aout table ain, 1,1,0,1
out ain,aout
"""

instrument('sin',1,SinStr)
instrument('sinsin',2,SinSinStr)

add(stopIns,startSco)

# *** Table ***
table('1. Sine wave',1,0,8192,10,1)

# *** Score ***
rem('Sine wave')
score(1,0,1)
rem('SinSin wave')
score(2,1,1)

add(stopSco,stopSyn)
writeRun('Tut22d')



This shows the wav output. With more complex interconnections even more varied signals result. In the next Tutorial, we will go over more complex examples of Phase Modulation.




You will find additional information at pythonaudio.blogspot.com, including the source code.



This is video of Tutorial 22.



About Me

I have used Python for the last 10+ years. I have a PhD in Electrical Engineering. I have taught Assembly Language programming of Intel-compatible chips as well as PC hardware interfacing. In my research, I have used Python to automate my calculations in physics and chemistry. I also use C++ and Java, often with Python.