Sunday, May 25, 2014

25. Filtered Noise


A noise source is used in all the examples. A Butterworth is a particular class of filters; they provide good audio quality. The particular filters, we shall use, are 2nd order. The higher the order, the more accurate but with more computations. With other csound opcodes, you can build any kind of filter.




There are four kinds of filter you can have. The first is the low-pass, which passes frequencies below some frequency, and rejects, that is, attenuates, higher frequencies.




The high-pass filter works in the opposite manner, to the low-pass, and lets the higher frequencies pass.




The third kind of filter is the band-pass. This let's frequencies, in a certain range pass, while, rejecting those outside a range. Here, we have to give the frequency corresponding to the middle of band, as well as the bandwidth.




The band-reject filter is opposite to the band-pass. This passes frequencies outside the range of the band.




Here, we have instrument strings for Noise, to serve as reference, and a low-pass filtered noise.




These are strings showing the other 3 band filters.




Finally, the reference, and the four filtered noise signals are added to different instruments.




This creates a score, calling each instrument for a 2 second duration.


# Filters.py

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

# *** Instrument Strings ***
WhiteNoiseString="""
asig rand 0.6
out asig, asig
"""

LowPassString="""
asig rand 0.6
alp  butterlp asig, 1000 ;passing frequencies below 1 KHz
out alp, alp
"""

HighPassString="""
asig rand 0.6
ahp  butterhp asig, 500 ;passing frequencies above 500 Hz
out ahp, ahp
"""

BandPassString="""
asig rand 0.6
abp  butterbp asig, 1000, 100 ;passing band from 950 to 1050 Hz
out abp, abp
"""

BandRejectString="""
asig rand 0.6
abr  butterbr asig, 10000, 10000 ;rejecting band from 5 kHz to 15 kHz
out abr, abr
"""

instrument('White Noise',1,WhiteNoiseString)
instrument('Low Pass',2,LowPassString)
instrument('High Pass',3,HighPassString)
instrument('Band Pass',4,BandPassString)
instrument('Band Reject',5,BandRejectString)

addTag(stopIns,startSco)

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

# *** Score ***
rem('Noise Filtering Score')
for i in range(5): score(i+1,2*i,2)

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




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



This is the video of Tutorial 25:


Wednesday, May 21, 2014

24. rand, buzz and gbuzz


rand, buzz and gbuzz are three opcodes producing frequency rich signals. They usually have to be filtered to remove frequencies, a topic covered later.




The rand opcode generates a random sequence. All the random values are between minus and positive of the first parameter, and the only required one.




This String uses a rand opcode, to generate a sequence between negative .5 and positive .5.




A noise signal will have a nearly uniform frequency spectrum.




The buzz opcode uses a sine input frequency, to create a signal with many harmonics. In xcps we give the fundamental frequency, and then we give the number of harmonics we need.




In this String using the buzz opcode, we request 3 harmonics of 440 Hz. The parameter value 1 indicates the table where the sine function is located, for our example.




In the frequency spectrum, we can see that there are three frequencies, 440 Hz, 2 times 440 Hz, and 3 times 440 Hz. All three have the same magnitude.




The gbuzz opcode creates a set of harmonics based on a cosine wave. We have to give it the fundamental frequency, number of harmonics in the output and the lowest harmonic that will be in the output. In addition, there is a parameter, kmul, which is a multiplying factor, controlling how harmonics increase or decrease.




This Instrument String, uses gbuzz to create 3 harmonics of 440 Hz, but we start with the 2nd harmonic (2 times 440 Hz) and the higher frequencies damp off with a multiplying factor of 0.7. We use the cosine table, which is numbered 2, in our program.


# Main.py

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

# *** Instrument Strings ***

randStr="""
asig rand 0.5
out asig
"""

buzzStr="""
asig buzz 1, 440, 3, 1
out asig 
"""

gbuzzStr="""
asig gbuzz 1, 440, 3, 2, 0.7, 2
out asig
"""


instrument('rand',1,randStr)
instrument('buzz',2,buzzStr)
instrument('gbuzz',3,gbuzzStr)

addTag(stopIns,startSco)

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

# *** Score ***
score(1,0,3)
score(2,3,3)
score(3,6,3)

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



This shows the three frequencies starting at 880 Hz (2 times 440 Hz) which are reducing at the rate of 0.7.


; Tut24.csd
<CsoundSynthesizer>
<CsOptions>
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 10
nchnls = 1
0dbfs = 1
; rand
instr 1
  asig rand 0.5
  out asig
endin
; buzz
instr 2
  asig buzz 1, 440, 3, 1
  out asig 
endin
; gbuzz
instr 3
  asig gbuzz 1, 440, 3, 2, 0.7, 2
  out asig
endin
</CsInstruments>
<CsScore>
; 1. Sine wave
f 1 0 8192 10 1
; 2. Cosine wave
f 2 0 8192 11 1 1
i 1 0 3
i 2 3 3
i 3 6 3
</CsScore>
</CsoundSynthesizer>



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



This is the video of Tutorial 24:


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.



Saturday, May 17, 2014

21. Frequency Modulation


Frequency Modulation results in audio with very different kinds of sounds.




The definition of frequency modulation is that a signal is frequency modulated, that is, we find a cosine of a sine. The signal to be modulated has some maximum modulating frequency, fm. So we have to analyze the signal y, given here.




There is a lot of math involved, involving imaginary numbers and expanding the sines to their Taylor Series. The term Jn(B) is a Bessel series. The term series means a number just like the exponent e, cosine of an angle, sine of an angle, or pi. We usually round to some decimal place as computers have finite precision. There is a Bessel series, that is, rounded to a number, for each integer n. The most important result, is that, now many frequencies result, with the form, fc plus n times fm, with n, being either a positive, or negative integer. This results in more frequencies, in our resulting signal, centered at the carrier frequency.




The most important csound FM opcode is foscil. Note, there are 6 required parameters and 1 optional parameter. The optional parameter should be left out so it defaults to 0. Also the kcps term should always be 1, so that, the inputs xcar and xmod, will refer to fc and fm. The input, xamp, is the amplitude and usually an envelope. The input, kndx, is the value, B, in the FM definition. B is called the modulating index and the larger the index, the more frequencies should be in the resulting signal. One of the important things to note, in FM synthesis, is that the resulting frequencies, should not hit the Nyquist frequency of 22,050 Hz so all frequencies are correctly mapped and not shifted down. This artifact is called aliasing. Here, we assumed the sampling frequency is the usual 44,100 Hz and thus the Nyquist is half of that.




There are two examples that we will go into. The first uses a carrier frequency of 2200 Hz, modulating frequency of 10 Hz, and an index of 10. The envelope goes from 0.1 to 0.3 with fade-in and fade-out, at either end.


# Main1.py

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

# *** Instrument String ***
FMStr="""
kenv linseg 0,0.1,0.1,p3-.2,0.3,0.1,0
aosc foscil kenv, 1, 2200, 10, 10, 1
out aosc, aosc
"""

instrument('',1,FMStr)

add(stopIns,startSco)

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

# *** Score ***
rem('Example for FM synthesis')
score(1,0,2)

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



In the next example, a String Template is created. There are three placeholders representing the carrier frequency, modulating frequency and modulating index.




Now, 150 different, but similar, instruments are created. The carrier frequency is always 2200 Hz, and the modulating index is always 10. Only the modulating frequency changes from 400 Hz to 251 Hz. Note, in the instrument call an empty String is passed so extra comment are not included on each line in the csound file. Now, the comment has been moved outside, in a rem() function call.




Finally, a score line is entered for each instrument. Each score line lasts for 0.4 seconds and are called in succession.


# Main2.py

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

# *** Instrument String Template ***
FMStr="""
kenv linseg 0,0.1,0.1,p3-.2,0.3,0.1,0
aosc foscil kenv, 1, %s, %s, %s, 1
out aosc, aosc
"""

# 150 Instrument Strings
FMString=[FMStr % (2200,400-i,10) for i in range(150)]

rem('150 FM Instrument Definitions')
for i in range(150): instrument('',i+1,FMString[i])

add(stopIns,startSco)

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

# *** Score ***
rem('Example for FM synthesis')
for i in range(150): score(i+1,i*.4,.4)

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



If you were to look at the csound file, it will be over 900 lines. The Python program is only 30 lines. We can make the Python program smaller by removing empty lines, etc. But this results in more confusing code. We only have to type the Python lines correctly, rather than typing everything correctly in the csound file. This would result in less debugging time.




This is the output wav file for a small time duration. The 150 different instruments, have output sounds, in very different sound color, that is, timbre.




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



This is the video of Tutorial 21:


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.