Fortran for the Common Man
I modeled a trumpet with the function
A(t)=\sum_n A_n\sum_i g_i \exp[i f_n \omega_i(t-t_n)]\exp[-(t-t_n)^2/d_n^2]({\rm erf}[(t-t_n)/s]+1)
using the dominant frequencies \omega_i and complex amplitudes g_i obtained from the Fourier transform of a real trumpet, and added this together for the notes n in Copland’s Fanfare for the Common Man with the frequency formula
f_n=f_02^{n/12}
where f_0=466.16 Hz – the frequency at which the trumpet was sampled.
Here’s the result:
program Fortran_for_the_Common_Man
implicit none
real, allocatable :: t(:),at(:),notes(:,:),w(:)
complex, allocatable :: g(:)
integer nw,nn,sr,nt,i,j
real tmax,atm,dt
! dominant frequencies and amplitudes of a sampled trumpet in A#4 (466.16 Hz)
w=[5852.79, 8780.75, 2927.96, 5874.78, 8809.03, 5892.06, 14635.11, 11743.27, &
2945.24, 11711.86, 17564.64, 8763.47, 8831.02, 20491.04, 14708.94, 11760.55, &
14668.10, 11802.96, 14734.07, 8741.48, 5909.34, 14693.23, 5833.94, 8719.49, &
11821.81, 17581.92, 14751.35, 17547.37, 20467.48, 11677.30, 5934.47, 11787.26,&
2912.26, 8848.30, 5816.66, 11694.58, 14768.63, 17597.63, 11647.45, 17652.61, &
17616.48, 5797.81, 11837.52, 20525.60, 8689.65, 11625.46, 23420.57, 32215.46, &
17635.33, 14609.98, 5973.74, 17522.23, 2968.81, 5956.46, 8879.71, 20437.63, &
17677.74, 9401.22, 26398.80, 98111.94, 3026.92, 20582.14, 20508.32, 8909.56, &
20610.42, 29285.93, 17495.53, 3044.20, 23510.11, 20649.69, 20550.73, 35143.43,&
95188.69, 2998.65, 29260.79, 14785.91, 3091.33, 32191.90, 11548.49, 26348.54, &
8648.80, 26312.41, 14555.00, 11873.65, 9418.49, 8926.84, 11854.80, 2883.98, &
11573.63, 26331.26]
g=[(-12.847,73.162), (12.656,66.056), (-23.249,-36.155), (15.392,-29.912), &
(-9.734,29.441), (-10.744,-22.579), (-15.942,18.515), (1.342,-21.755), &
(-6.791,-19.484), (-7.380,-19.161), (-5.987,-19.299), (-16.223,6.601), &
(-2.903,14.915), (-12.222,-6.706), (13.797,1.916), (4.005,-12.722), &
(-11.090,-6.728), (5.215,11.284), (-6.107,10.629), (-9.961,-6.950), &
(-10.454,0.239), (3.417,9.764), (2.810,9.689), (-1.275,-9.433), &
(6.637,5.847), (6.224,-5.807), (-2.296,7.834), (-1.440,7.980), &
(-6.721,-4.521), (4.822,6.308), (7.258,-1.395), (1.987,6.964), &
(-1.086,7.157), (6.035,3.417), (-6.105,-2.844), (-2.598,-6.208), &
(1.848,6.094), (2.791,-5.262), (5.642,-1.187), (5.346,-1.735), &
(2.827,4.786), (1.816,-5.042), (3.186,-4.157), (2.638,-4.399), &
(-4.582,-1.908), (3.117,3.826), (-3.858,-2.813), (-3.107,-3.526), &
(4.307,1.661), (-4.192,-1.810), (-4.278,-0.335), (-3.431,2.212), &
(-2.222,-3.244), (2.699,-2.672), (2.997,-1.720), (-2.918,1.811), &
(2.379,2.108), (-3.148,-0.344), (1.851,-2.560), (-0.129,-3.147), &
(-2.952,0.815), (1.090,2.855), (2.997,0.341), (2.929,0.608), &
(-2.493,-1.624), (-2.771,0.838), (-1.860,2.179), (2.589,1.162), &
(0.492,2.790), (-0.055,-2.769), (-2.245,-1.594), (0.044,-2.660), &
(-2.604,0.409), (-2.566,-0.530), (-2.241,1.320), (2.418,0.954), &
(-2.043,-1.496), (-0.340,-2.474), (-2.477,-0.181), (-1.400,-1.984), &
(0.551,2.364), (-2.192,0.925), (-2.196,0.807), (-1.703,-1.589), &
(0.695,2.207), (-0.914,-2.125), (-0.725,-2.189), (-0.246,2.268), &
(-1.769,1.429), (-0.640,2.177)]
! number of frequencies and amplitudes
nw=size(w)
! excerpt from Fanfare for the Common Man by Aaron Copland
! note, amplitude, duration, time
notes=reshape([ &
-2., 0.78, 0.078, 0.500, 5., 0.86, 0.093, 0.694, 10., 0.94, 1.548, 0.886, &
3., 0.77, 0.090, 2.032, 10., 0.72, 0.102, 2.224, 8., 0.89, 2.244, 2.416, &
12., 0.89, 1.098, 3.956, 8., 0.91, 1.137, 4.718, 3., 0.94, 1.128, 5.488, &
-2., 0.86, 2.691, 6.260, -4., 0.80, 0.102, 8.176, 0., 0.83, 0.099, 8.374, &
3., 0.88, 1.347, 8.580, 3., 0.88, 0.099, 9.708, 7., 0.80, 0.108, 9.904, &
10., 0.95, 1.338, 10.096, -4., 0.80, 0.096, 11.240, 0., 0.84, 0.108, 11.428, &
3., 0.89, 1.311, 11.632, 7., 0.89, 0.255, 12.782, -2., 0.86, 0.282, 13.164, &
-9., 0.90, 1.539, 13.572 ], [4,22])
! number of notes
nn=size(notes,2)
! maximum time in seconds
tmax=maxval(notes(4,1:nn))+5.
! sample rate
sr=44100
! number of time steps
nt=tmax*sr
allocate(t(nt),at(nt))
! time steps
do i=1,nt
t(i)=tmax*real(i-1)/nt
end do
! sum the notes to get the total audio amplitude as a function of time
write(*,'("Generating time-dependent audio amplitude...")')
at(:)=0.0
do i=1,nt
do j=1,nn
if (abs(t(i)-notes(4,j)) > 8.*notes(3,j)) cycle
at(i)=at(i)+atn(j,t(i))
end do
end do
! find the maximum amplitude
atm=maxval(abs(at(:)))
! normalize to 1
at(:)=at(:)/atm
! write to file in PCM 32-bit floating-point little-endian format
write(*,'("Writing to file...")')
open(50,file='fftcm.f32',access='STREAM',status='REPLACE')
do i=1,nt
write(50) at(i)
end do
close(50)
! uncomment either line to play from the Fortran executable
!call execute_command_line('sox -r 44100 -c 1 -e floating-point fftcm.f32 -d')
!call execute_command_line('ffplay -ar 44100 -f f32le fftcm.f32')
contains
pure real function atn(j,t)
implicit none
integer, intent(in) :: j
real, intent(in) :: t
real, parameter :: s=0.05 ! skewness
real n,a,d,t0,dt,f
n=notes(1,j); a=notes(2,j); d=notes(3,j); t0=notes(4,j)
dt=t-t0
! frequency in terms of number of semitones away from A#4
f=2.**(n/12.)
! model trumpet note
atn=a*real(sum(g(:)*exp((0.,1.)*f*w(:)*dt)))*exp(-(dt/d)**2)*(erf(dt/s)+1.)
end function
end program
It compiles and runs with both Intel Fortran and GFortran. The audio file can be played with either
sox -r 44100 -c 1 -e floating-point fftcm.f32 -d
or
ffplay -ar 44100 -f f32le fftcm.f32
Alternatively, uncomment a ‘call execute_command_line’ line in the code and the Fortran executable will play it for you.
Here’s a challenge for someone: Fortran the Brave on Bagpipes.