How to remove the noise from wave file - python

I have a sound file, I apply a high frequency filter
import wave
import scipy.io.wavfile as wav
import numpy as np
import scipy as sp
origAudio = wave.open('3734.wav','r')
frameRate = origAudio.getframerate()
nChannels = origAudio.getnchannels()
sampWidth = origAudio.getsampwidth()
nbframe=origAudio.getnframes()
da = np.fromstring(origAudio.readframes(frameRate), dtype=np.int16)
left, right = da[0::2], da[1::2]
b, a = signal.butter(2, 0.03,btype='highpass', analog=False)
left = signal.filtfilt(b, a, left)
hann = np.hanning(len(left))
l,f=np.fft.rfft(left*hann), np.fft.rfft(right)
plt.xscale('log')
plt.xlabel('frequency [Hz]')
plt.ylabel('|Magnitude|')
plt.plot(np.abs(l))
I obtain
However I want to remove the noise from the signal , abtain only the peaks.Thanks

Related

find wave discontinuity by using fourier transform( wav file's tick noise)

audio_sample, sampling_rate = librosa.load('a.wav', sr=None)
S = np.abs(librosa.stft(audio_sample, n_fft=1024, hop_length=512, win_length=1024, window=signal.hann))
mag_db = librosa.amplitude_to_db(S)
mag_n = _normalize(mag_db)
librosa.display.specshow(mag_n, y_axis='linear', x_axis='time', sr=sampling_rate)
I did some stft and then spechow. but I want to find the point(time) where wave discontinuity.
example) I want to know discontinuity point of the wave file
I mean like this
You just need to use derivatives to find discontinuities in a wave. To take the derivative of the wave we can use convolution with a filter.
Here's a sample code:
import numpy as np
import matplotlib.pyplot as plt
# generating the wave
angle1 = np.linspace(0, 5*np.pi/2, 100)
wave1 = np.sin(angle1)
angle2 = np.linspace(0, 3*np.pi/2)
wave2 = np.sin(angle2)
angle3 = np.linspace(np.pi/2, 2*np.pi)
wave3 = np.sin(angle3)
wave = np.append(wave1, wave2)
wave = np.append(wave, wave3)
# differentiation
filter = [-1, 1]
dif = np.convolve(wave, filter)
absdif = np.abs(dif)
print('discontinuity at:', np.where(absdif > 0.75)[0])
plt.subplot(3, 1, 1)
plt.plot(wave, label='input wave')
plt.subplot(3, 1, 2)
plt.plot(dif, label='dirivative')
plt.subplot(3, 1, 3)
plt.plot(absdif, label='absolut of derivative')
plt.show()
Output graph:

From Amplitude or FFT to dB

I've a Python code which performs FFT on a wav file and plot the amplitude vs time / amplitude vs freq graphs. I want to calculate dB from these graphs (they are long arrays). I do not want to calculate exact dBA, I just want to see a linear relationship after my calculations. I've dB meter, I will compare it. Here is my code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import scipy.io.wavfile as wavfile
import scipy
import scipy.fftpack
import numpy as np
from matplotlib import pyplot as plt
fs_rate, signal = wavfile.read("output.wav")
print ("Frequency sampling", fs_rate)
l_audio = len(signal.shape)
print ("Channels", l_audio)
if l_audio == 2:
signal = signal.sum(axis=1) / 2
N = signal.shape[0]
print ("Complete Samplings N", N)
secs = N / float(fs_rate)
print ("secs", secs)
Ts = 1.0/fs_rate # sampling interval in time
print ("Timestep between samples Ts", Ts)
t = scipy.arange(0, secs, Ts) # time vector as scipy arange field / numpy.ndarray
FFT = abs(scipy.fft(signal))
FFT_side = FFT[range(N//4)] # one side FFT range
freqs = scipy.fftpack.fftfreq(signal.size, t[1]-t[0])
fft_freqs = np.array(freqs)
freqs_side = freqs[range(N//4)] # one side frequency range
fft_freqs_side = np.array(freqs_side)
makespositive = signal[44100:]*(-1)
logal = np.log10(makespositive)
sn1 = np.mean(logal[1:44100])
sn2 = np.mean(logal[44100:88200])
sn3 = np.mean(logal[88200:132300])
sn4 = np.mean(logal[132300:176400])
print(sn1)
print(sn2)
print(sn3)
print(sn4)
abs(FFT_side)
for a in range(500):
FFT_side[a] = 0
plt.subplot(311)
p1 = plt.plot(t[44100:], signal[44100:], "g") # plotting the signal
plt.xlabel('Time')
plt.ylabel('Amplitude')
plt.subplot(312)
p1 = plt.plot(t[44100:], logal, "r") # plotting the signal
plt.xlabel('Time')
plt.ylabel('Amplitude')
plt.subplot(313)
p3 = plt.plot(freqs_side, abs(FFT_side), "b") # plotting the positive fft spectrum
plt.xlabel('Frequency (Hz)')
plt.ylabel('Count single-sided')
plt.show()
First plot is amplitude vs time, second one is logarithm of previous graph and the last one is FFT.
In sn1,sn2 part I tried to calculate dB from signal. First I took log and then calculated mean value for each second. It did not give me a clear relationship. I also tried this and did not worked.
import numpy as np
import matplotlib.pyplot as plt
import scipy.io.wavfile as wf
fs, signal = wf.read('output.wav') # Load the file
ref = 32768 # 0 dBFS is 32678 with an int16 signal
N = 8192
win = np.hamming(N)
x = signal[0:N] * win # Take a slice and multiply by a window
sp = np.fft.rfft(x) # Calculate real FFT
s_mag = np.abs(sp) * 2 / np.sum(win) # Scale the magnitude of FFT by window and factor of 2,
# because we are using half of FFT spectrum
s_dbfs = 20 * np.log10(s_mag / ref) # Convert to dBFS
freq = np.arange((N / 2) + 1) / (float(N) / fs) # Frequency axis
plt.plot(freq, s_dbfs)
plt.grid(True)
So which steps should I perform? (Sum/mean all freq amplitudes then take log or reverse, or perform it for signal etc.)
import numpy as np
import matplotlib.pyplot as plt
import scipy.io.wavfile as wf
fs, signal = wf.read('db1.wav')
signal2 = signal[44100:]
chunk_size = 44100
num_chunk = len(signal2) // chunk_size
sn = []
for chunk in range(0, num_chunk):
sn.append(np.mean(signal2[chunk*chunk_size:(chunk+1)*chunk_size].astype(float)**2))
print(sn)
logsn = 20*np.log10(sn)
print(logsn)
Output:
[4.6057844427695475e+17, 5.0025315250895744e+17, 5.028593412665193e+17, 4.910948397471887e+17]
[353.26607217 353.98379668 354.02893044 353.82330741]
A decibel meter measures a signal's mean power. So from your time signal recording you can calculate the mean signal power with:
chunk_size = 44100
num_chunk = len(signal) // chunk_size
sn = []
for chunk in range(0, num_chunk):
sn.append(np.mean(signal[chunk*chunk_size:(chunk+1)*chunk_size]**2))
Then the corresponding mean signal power in decibels is simply given by:
logsn = 10*np.log10(sn)
A equivalent relationship could also be obtained for a frequency domain signal with the use of Parseval's theorem, but in your case would require unecessary FFT computations (this relationship is mostly useful when you already have to compute the FFT for other purposes).
Note however that depending on what you compare there may be some (hopefully small) discrepancies. For example the use of non-linear amplifier and speakers would affect the relationship. Similarly ambient noises would add to the measured power by the decibel meter.

How to envelope a non periodical signal?

I want to analyse a trc oscilloscope file, find impulses and envelope them. In the end I want to plot the envelope.
data file (trc): https://ufile.io/z4m4d
Code:
import matplotlib.pyplot as plt
import pandas as pd
import readTrc
import numpy as np
from scipy.signal import hilbert
#Read trc file
datX, datY, m = readTrc.readTrc('C220180104_ch2_UHF00014.trc')
srx, sry = pd.Series(datX * 1000), pd.Series(datY * 1000)
df = pd.concat([srx, sry], axis = 1)
df.set_index(0, inplace = True)
#Impulse location
x1 = df[1].idxmax() - 0.0005 #numeric used to show area before impulse
x2 = df[1].idxmax() + 0.003 #numeric used to show area after impulse
df2 = df.loc[x1:x2]
#Locate Maximum
print('Maximum at:', round(df[1].idxmax(), 6), 'ms')
#Plot Impulse (abs)
df3 = df2.abs().interpolate()
df3.plot.area(grid = 1,
linewidth = 0.5)
#Envelope
signal = hilbert(df2)
envelope = np.abs(signal)
df4 = pd.DataFrame(envelope)
df4.plot(color = 'red')
plt.xlabel('Zeit / ms')
plt.ylabel('UHF-Signal / mV')
##plt.savefig('UHF_plot.png', dpi = 600)
plt.show()
print('done')
The Output does not look like an envelope.
Plot:
Envelope:
Edit:
This is an approximation of what I want.
Envelope demodulation requires bandpassing before the hilbert transform. For your case, I believe that low-passing your "envelope signal" will bring you to the required result.
You can also create an semi-envelope using find_peaks but I assume that it was not your intention.

Not able to recreate same sound using FFT

I am trying to recreate musical note using top 10 frequencies returned by Fourier Transform (FFT). Resulting sound does not match the original sound. Not sure if I am not finding frequencies correctly or not generating sound from it correctly. The goal of this code is to match the original sound.
Here is my code:
import numpy as np
from scipy.io import wavfile
from scipy.fftpack import fft
import matplotlib.pyplot as plt
i_framerate = 44100
fs, data = wavfile.read('./Flute.nonvib.ff.A4.stereo.wav') # load the data
def findFrequencies(arr_data, i_framerate = 44100, i_top_n =5):
a = arr_data.T[0] # this is a two channel soundtrack, I get the first track
# b=[(ele/2**8.)*2-1 for ele in a] # this is 8-bit track, b is now normalized on [-1,1)
y = fft(a) # calculate fourier transform (complex numbers list)
xf = np.linspace(0,int(i_framerate/2.0),int((i_framerate/2.0))+1) /2 # Need to find out this last /2 part
yf = np.abs(y[:int((i_framerate//2.0))+1])
plt.plot(xf,yf)
yf_top_n = np.argsort(yf)[-i_top_n:][::-1]
amp_top_n = yf[yf_top_n] / np.max(yf[yf_top_n])
freq_top_n = xf[yf_top_n]
return freq_top_n, amp_top_n
def createSoundData(a_freq, a_amp, i_framerate=44100, i_time = 1, f_amp = 1000.0):
n_samples = i_time * i_framerate
x = np.linspace(0,i_time, n_samples)
y = np.zeros(n_samples)
for i in range(len(a_freq)):
y += np.sin(2 * np.pi * a_freq[i] * x)* f_amp * a_amp[i]
data2 = np.c_[y,y] # 2 Channel sound
return data2
top_freq , top_freq_amp = findFrequencies(data, i_framerate = 44100 , i_top_n = 200)
print('Frequencies: ',top_freq)
print('Amplitudes : ',top_freq_amp)
soundData = createSoundData(top_freq, top_freq_amp,i_time = 2, f_amp = 50 / len(top_freq))
wavfile.write('createsound_A4_v6.wav',i_framerate,soundData)
The top 10 spectral frequencies in a musical note are not the same as the center frequencies of the top 10 FFT result bin magnitudes. The actual frequency peaks can be between the FFT bins.
Not only can the frequency peak information be between FFT bins, but the phase information required to reproduce any note transients (attack, decay, etc.) can also be between bins. Spectral information that is between FFT bins is carried by a span (up to the full width) of the complex FFT result.

Plotting spectrum of a signal

from numpy.fft import fft
from numpy import array
a = array([1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]
print( ' '.join("%5.3f" % abs(f) for f in fft(a)) )
I am using this code to get fft of a signal but how can I plot the fft.
Thanks
Here is an example I wrote to plot a wav file.
It is slightly more complicated because it deals with a stereo sound file by averaging the left and right channels. And it plots both the signal and the fft.
from __future__ import print_function, division
import wave
import numpy as np
import matplotlib.pyplot as plt
wr = wave.open('input.wav', 'r')
sz = wr.getframerate()
q = 5 # time window to analyze in seconds
c = 12 # number of time windows to process
sf = 1.5 # signal scale factor
for num in range(c):
print('Processing from {} to {} s'.format(num*q, (num+1)*q))
avgf = np.zeros(int(sz/2+1))
snd = np.array([])
# The sound signal for q seconds is concatenated. The fft over that
# period is averaged to average out noise.
for j in range(q):
da = np.fromstring(wr.readframes(sz), dtype=np.int16)
left, right = da[0::2]*sf, da[1::2]*sf
lf, rf = abs(np.fft.rfft(left)), abs(np.fft.rfft(right))
snd = np.concatenate((snd, (left+right)/2))
avgf += (lf+rf)/2
avgf /= q
# Plot both the signal and frequencies.
plt.figure(1)
a = plt.subplot(211) # signal
r = 2**16/2
a.set_ylim([-r, r])
a.set_xlabel('time [s]')
a.set_ylabel('signal [-]')
x = np.arange(44100*q)/44100
plt.plot(x, snd)
b = plt.subplot(212) # frequencies
b.set_xscale('log')
b.set_xlabel('frequency [Hz]')
b.set_ylabel('|amplitude|')
plt.plot(abs(avgf))
plt.savefig('simple{:02d}.png'.format(num))
plt.clf()
Below is one of the plots it generated. Colours et cetera are different from the default because of my custom matplotlibrc.

Categories

Resources