Saturday, May 17, 2014

20. Amplitude Modulation


csound has many opcodes related to frequency modulation. To understand frequency modulation, you have to understand the amplitude modulation.




Amplitude Modulation is used in communications to send a signal over distances. The signal that we want to send has some maximum frequency, say f1. It will be modulated, that is, multiplied, by a frequency f2, acting as our carrier.




The result of multiplying two sines is the sum and difference of the original frequencies. The fact that it is cosine is not important as cosine is just a sine with a phase, that is, a time offset.




In Amplitude Modulation, a signal, such as an audio, is modulated to higher frequencies. It is propagated by an antenna as an electromagnetic signal.




If the two frequencies are almost equal, beats result as one of the output is seen as a low-frequency signal, which is seen as an envelope to a much higher signal, which is about 2 times the frequency of the original signal.




The first csound example will have frequencies of 2000 Hz and 500 Hz. We know the output should be a combination of 1500 Hz and 2500 Hz. We can relate all this to csound opcodes, since multiplying two sines is the same as feeding one oscil into another.




This is the Instrument String. First the linseg opcode is used to create an envelope. The amplitude is 0.7 except at the ends where it goes to 0. Then, two envelopes are created for the cosine waves. Audio for left channel is oscil output of another oscil. The right channel is the sum of two cosines, at 1500 Hz and 2500 Hz.




These are the two tables used here. Table 2 uses GEN 11 which is a cosine signal generator.




This is the output wave file. The two channels are seen to be indeed identical.




The frequency spectrum, shows that there are two frequencies, and they are at 1500 Hz and 2500 Hz.




We can find the period of the wave by selecting the portion of the audio that repeats. It is of width 88 samples. Since there are 44,100 samples in one second this is 44,100 divided by 88 or 500 Hz. This make sence since 1500 Hz is the third harmonic of 500 Hz, and 2500 Hz is the fifth harmonic of 500 Hz.




This is the Instrument String for the case that the two frequencies are nearly identical.


# Main.py

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

# *** Instrument String ***
AmpModEx="""
aenv linseg 0,0.1,0.7,p3-.2,0.7,0.1,0
aenv1 = 0.5*aenv
aenv2 = -.5*aenv
aoscil1 oscil aenv,2000,1
aL oscil aoscil1,1950,1
acos1 oscil aenv1,50,2
acos2 oscil aenv2,3950,2
aR = acos1 + acos2
out aL,aR
"""
instrument('1. AM example',1,AmpModEx)

add(stopIns,startSco)

# *** Table ***
rem('Table 1 uses GEN 10 (sines)')
table('1. Sine wave',1,0,8192,10,1)
rem('Table 2 uses GEN 11 (cosines)')
table('2. Cosine wave',2,0,8192,11,1)

# *** Score ***
rem('Amplitude Modulation')
score(1,0,2)

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



From the output wave file, we can see the low-frequency signal acting as an envelope for the higher modulating signal.


; Tut20
<CsoundSynthesizer>
<CsOptions>
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 10
nchnls = 2
0dbfs = 1
; 1. AM example
instr 1
  aenv linseg 0,0.1,0.7,p3-.2,0.7,0.1,0
  aenv1 = 0.5*aenv
  aenv2 = -.5*aenv
  aoscil1 oscil aenv,2000,1
  aL oscil aoscil1,1950,1
  acos1 oscil aenv1,50,2
  acos2 oscil aenv2,3950,2
  aR = acos1 + acos2
  out aL,aR
endin
</CsInstruments>
<CsScore>
; Table 1 uses GEN 10 (sines)
; 1. Sine wave
f 1 0 8192 10 1
; Table 2 uses GEN 11 (cosines)
; 2. Cosine wave
f 2 0 8192 11 1
; Amplitude Modulation
i 1 0 2
</CsScore>
</CsoundSynthesizer>



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



This is the video of Tutorial 20:



Wednesday, May 14, 2014

19. Function Tables


Now we will talk about some opcodes used to read tables. A table is an array of some given size. A table could be a calculated function or a sampled audio wave. Here, we will use sine functions.




The print opcode is used to print i-time variables, such as p fields from the score i-lines.




The phasor opcode creates a linear changing line which repeats at some frequency. The slope of the line is determined by the given frequency in the paramater list. A phasor opcode has two arguments, and only 1 is necessary. Usually the phase argument is left out so the line starts at 0. The x for the audio format, means it can accept i-time, k-rate or a-rate variables. The kcps terms means the frequency can be either k-rate or i-time.





This is an Instrument String. First, the 3 p-values are printed, and a phasor of 1 Hz is created for the audio channels.




The table opcode can be used to read a table. The first parameter is the index, which can either be a raw value or a normalized value. In the example, we use the normalized version, so the mode is 1. The index goes from 0 to 1 as in the phasor function.




This is an Instrument multi-line String where we have a phasor acting as an index term for the two audio rate variables. These two audio-rate variables, lookup table 1 and table 2.




One of the most often used opcodes is oscil. The function of oscil is to combine the functions of the phasor and table opcodes. In addition, there is an amplitude term.




This is the instrument String, using the oscil opcode. We don't have to create new i-time variables, as we did here. We could just as easily enter constants directly in the oscil opcode. Like here, you might use them for clarity.




Notice, the instrument() function has changed. It now has 3 parameters, with the first parameter being a comment string.




Next, we have two calls to the table function. This will create the two tables that will be used. The table functions uses the GEN 10 routine which is a sum, of sines, at different harmonics. They were covered in Tutorial 6. The first table is a single sine wave and the second table is different harmonics composing a square wave.




This is the table function. There is a documentation string indicating it creates f-lines in a score section. We use f-lines to create different tables. The function is very similar to the i-line, except now a table is generated.




After importing the module, we don't have to remember the ordering of parameters, since writing table and starting the parenthesis, in ipython, gives the format and doc string.




The score has examples for the three instruments. Now we have an extra function, rem() to insert a comment in the csd file.


# 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 add(*b):
    """
    Adding terms to csd file
    """
    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



The text output, as captured by the Anaconda Command Prompt, shows the output due to the print statements.


# Main.py

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

# *** Instrument Strings ***

PhasorEx="""
print p1,p2,p3
aphas phasor 1
out aphas,aphas
"""
TableEx="""
print p1,p2,p3
aphas phasor 1
atab1 table aphas,1,1
atab2 table aphas,2,1
out atab1,atab2
"""
OscilEx="""
iamp=0.5
ifreq=2
print p1,p2,p3,iamp,ifreq
aoscil1 oscil iamp,ifreq,1
aoscil2 oscil iamp,ifreq,2
out aoscil1,aoscil2
"""

instrument('1. phasor example',1,PhasorEx)
instrument('2. table example',2,TableEx)
instrument('3. oscil example',3,OscilEx)

add(stopIns,startSco)

# *** Table ***
table('1. Sine wave',1,0,1024,10,1)
table('2. Square wave',2,0,1025,10,1,0,0.33,0,0.20,0,0.14)

# *** Score ***
rem('Examples for phasor, table, oscil')
score(1,0,2)
score(2,2,2)
score(3,4,2)

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



Finally, running the csd file results in this wave file.


<CsoundSynthesizer>
<CsOptions>
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 10
nchnls = 2
0dbfs = 1
; 1. phasor example
instr 1
  print p1,p2,p3
  aphas phasor 1
  out aphas,aphas
endin
; 2. table example
instr 2
  print p1,p2,p3
  aphas phasor 1
  atab1 table aphas,1,1
  atab2 table aphas,2,1
  out atab1,atab2
endin
; 3. oscil example
instr 3
  iamp=0.5
  ifreq=2
  print p1,p2,p3,iamp,ifreq
  aoscil1 oscil iamp,ifreq,1
  aoscil2 oscil iamp,ifreq,2
  out aoscil1,aoscil2
endin
</CsInstruments>
<CsScore>
; 1. Sine wave
f 1 0 1024 10 1
; 2. Square wave
f 2 0 1025 10 1 0 0.33 0 0.2 0 0.14
; Examples for phasor, table, oscil
i 1 0 2
i 2 2 2
i 3 4 2
</CsScore>
</CsoundSynthesizer>



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



This is the video of Tutorial 19:


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.