junilyd junilyd - 1 month ago
92 0

Interface for musicians to play music and explore music

Python

Pitch Box

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.collections as collections
import pyaudio
import struct
from scipy.signal import get_window
from scipy.fftpack import fft, ifft

M = 512 #Blocksize
WIDTH = 2 #2 bytes per sample
CHANNELS = 1
fs = 4000
N=2**12
L = 3;
w  = get_window('blackman', M)

updateTime = 10 # 10ms
xDuration = 20
lowOctave = 2;
numOctaves = 2;

C1 = 440.0*2**(-45/12.0)
minPitch = C1*2**(4/12.0)*lowOctave
maxPitch = minPitch*2**(numOctaves)
def data_gen(t=0):
    cnt = 0
    while cnt < xDuration*10: # 
        cnt += 1
        t += 0.1
        yield t


def init():
    ax.set_ylim(minPitch, maxPitch)
    ax.set_xlim(0, xDuration)
    ax.set_yscale('log')
    ax.set_xlabel('Time (sec)')
    ax.set_ylabel('Pitch (Hz)')
    del xdata[:]
    del ydata[:] 

    line.set_data(xdata, ydata)
    return line,

fig, ax = plt.subplots()
line, = ax.plot([], [], '*',markersize=10,markeredgecolor = 'white',lw=2)
ax.grid()
xdata, ydata = [], []

# color background for half notes
for octave in range(lowOctave,lowOctave+numOctaves+1):
    C = C1*2**(octave-1)
    ax.axhspan(C             , C*2**(1.0/12) ,facecolor='red', alpha=0.9)   
    ax.axhspan(C*2**(1.0/12) , C*2**(2.0/12) ,facecolor='red', alpha=0.6)
    ax.text(20.1,C,'C%s'%(octave)) 
    ax.axhspan(C*2**(2.0/12) , C*2**(3.0/12) ,facecolor='orange', alpha=1.0)
    ax.axhspan(C*2**(3.0/12) , C*2**(4.0/12) ,facecolor='orange', alpha=0.7)
    ax.text(20.1,C*2**(2.0/12),'D%s'%(octave)) 
    ax.axhspan(C*2**(4.0/12) , C*2**(5.0/12) ,facecolor='yellow', alpha=0.9)
    ax.text(20.1,C*2**(4.0/12),'E%s'%(octave))
    ax.axhspan(C*2**(5.0/12) , C*2**(6.0/12) ,facecolor='green', alpha=0.9)
    ax.axhspan(C*2**(6.0/12) , C*2**(7.0/12) ,facecolor='green', alpha=0.4)
    ax.text(20.1,C*2**(5.0/12),'F%s'%(octave))
    ax.axhspan(C*2**(7.0/12) , C*2**(8.0/12) ,facecolor='blue', alpha=0.9)
    ax.axhspan(C*2**(8.0/12) , C*2**(9.0/12) ,facecolor='blue', alpha=0.4)
    ax.text(20.1,C*2**(7.0/12),'G%s'%(octave))
    ax.axhspan(C*2**(9.0/12), C*2**(10.0/12),facecolor='purple', alpha=0.9)
    ax.axhspan(C*2**(10.0/12), C*2**(11.0/12),facecolor='purple', alpha=0.4)
    ax.text(20.1,C*2**(9.0/12),'A%s'%(octave))
    ax.axhspan(C*2**(11.0/12) , C*2**(12.0/12) ,facecolor='pink', alpha=0.4)
    ax.text(20.1,C*2**(11.0/12),'B%s'%(octave))

def smc_ANLS(X, N, minf0, maxf0, fs, L=5, freqRes=1.0):
    """
    Pitch estimation by Maximum Likelihood ANLS  
    X: Fourier transform of a sound, 
    N: FFT size, minf0: minimum f0 frequency in Hz, 
    maxf0: maximim f0 frequency in Hz,
    L: number of harmonics to use including fundamental,
    freqRes: resolution in Hz in the search space.
    returns pitch: pitch/f0 estimate
    """
    #f0Area = np.linspace(minf0,maxf0, (maxf0-minf0)*1/freqRes+1)
    maxf0Log = np.log10(maxf0)
    minf0Log = np.log10(minf0)
    f0Area = np.logspace(minf0Log,maxf0Log, 750)#(maxf0-minf0)*1/freqRes+1)
    pitch=[];

    cost=[]; 
    for f in f0Area:
        index=[];
        amplitudes=[];
        for l in np.linspace(1,L,L):
            index = int(np.floor(float(N)/fs*f*l)+1)
            amplitudes.append(X[index])
        cost.append(np.sum(amplitudes)); 
    I = np.argmax(cost);
    return f0Area[I]

def run(data):
    # update the data
    signal = stream.read(M)
    shorts = (struct.unpack( 'h' * M, signal ));
    x = np.array(list(shorts),dtype=float);
    x=x/(2**15-1.0)*w   
    X = abs(fft(x,N))
    # threshold on the swhitedard deviation in ff9 
    if np.std( X[int(np.floor(float(N)/fs*minPitch)+1):int(np.floor(float(N)/fs*maxPitch*L)+1)]) < 1.0:
        f0 = 0;    
    else:    
        f0 = smc_ANLS(X, N, minPitch, maxPitch, fs, L, 0.5)
        t = data
        xdata.append(t)
        ydata.append(f0)

        xmin, xmax = ax.get_xlim()
        if t >= xmax:
            ax.set_xlim(xmin+10, xmax+10)
            ax.figure.canvas.draw()            
        line.set_data(xdata, ydata)
    return line,

p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(WIDTH),
                channels=CHANNELS,
                rate=fs,
                input=True,
                output=True,
                frames_per_buffer=M)

ani = animation.FuncAnimation(fig, run, data_gen, blit=False, interval=updateTime,
                              repeat=True, init_func=init)
plt.show()
stream.stop_stream()
stream.close()
p.terminate()
Comments