@TetsuyaMiwa you are right. Although Pyqtgraph should be significantly more performant then matplotlib, performance with both libraries is poor because there is another bottleneck in play. I think the slowest operation here is a cs.tableCopyOut() command. I benchedmarked it and it takes a lot of time and that time is also not deterministic. It varies from 0.05 to 3.0 seconds. This is definitely an interesting point. Does anyone know why is that?
code for benchmarking:
from timeit import default_timer as timer
.
.
start = timer()
cs.tableCopyOut(1,fftArray)
end = timer()
print(end - start)
I mean, I understand that running Csound in its own thread (a process would be maybe a more correct term here) gives a significant gain on audio performance in a cost of a more expensive communication (data input/output) between python and csound threads but those times are extreme.
On the other hand, if we run csound inside the python process we can expect audio performance issues but communication is much much faster. Here is a code how you can implement it. This code uses Matplotlib but same thing can be of course implemented with pyqtgraph also.
import ctcsound as csound
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
import threading
fftSize = 1024 # this needs to match gifftsiz in orc
fs = 48000 # sample rate
orc = """
0dbfs = 1
ksmps = 128
;general values for fourier transform
gifftsiz = 1024 ; this needs to match above defined fftSize
gioverlap = 256
giwintyp = 1 ;von hann window
;an 'empty' function table
giTable ftgen 1, 0, -(gifftsiz+2), 2, 0
instr 1
aout oscili 0.5, 500
aout *= linseg(0, 1, 1, p3-1, 0)
out aout
fsig pvsanal aout, gifftsiz, gioverlap, gifftsiz, giwintyp
kflag pvsftw fsig, giTable
endin
"""
def csound_function(name):
cs = csound.Csound()
cs.setOption('-odac')
cs.compileOrc(orc)
sco = "i1 0 5\n"
cs.readScore(sco)
cs.start()
while not cs.performKsmps():
cs.tableCopyOut(1,fftArray)
cs.reset()
x = threading.Thread(target=csound_function, args=(1,))
x.start()
fig, ax = plt.subplots()
ax.set(xlim=(0,fs/2), ylim=(0,1))
line, = ax.plot([], [], lw=2)
f_axis_delta = fs/fftSize
f_axis = np.arange(0,fs/2 + f_axis_delta,f_axis_delta)
fftArray = np.zeros(fftSize + 2) # array length must be fftSize+2
def animate(i, f_axis_cs=[], amp_vals=[]):
amp_vals = fftArray[0::2] # fft amplitude values
line.set_data(f_axis, amp_vals)
anim = animation.FuncAnimation(fig, animate, interval=100)
plt.show()