<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Static/Simulation-Based Audio Processing
Notch Filters to Remove Interference
Within sigsys there are some handy functions for designing single section notch filters and then cascading them. First set up a scenario with tone interference present in speech.
Step1:  Look at the waveform and then listen to it.
Step2:  Design a cascade of notch filters
Step3:  Now apply the filter to the composite signal
Step4:  Adaptive Interference Removal
(Placeholder, but start from ss.lms_ic)
```python
def lms_ic(r,M,mu,delta=1)
Step6:  The time varying delay in flanging takes the form
Step7:  Playback using PyAudio (one channel only)
<ASSISTANT_TASK:>
Python Code:
fs,s = ss.from_wav('OSR_us_000_0030_8k.wav')
soi = s[10000:40000]
n = arange(len(soi))
snoi = 0.4*cos(2*pi*1000/fs*n) + 0.5*cos(2*pi*1500/fs*n)
r = soi + snoi
psd(r,2**10,8000);
title(r'Two Interfering Tones');
Explanation: Static/Simulation-Based Audio Processing
Notch Filters to Remove Interference
Within sigsys there are some handy functions for designing single section notch filters and then cascading them. First set up a scenario with tone interference present in speech.
End of explanation
# First save r to a wave file for static playback
ss.to_wav('speech_tone.wav',8000,r/2)
Audio('speech_tone.wav')
Explanation: Look at the waveform and then listen to it.
End of explanation
plot(r[6000:10000])
title(r'The Interference is Overwhelming')
bn1, an1 = ss.fir_iir_notch(1000,8000)
bn2, an2 = ss.fir_iir_notch(1500,8000,.98) # tighten the bandwidth of the 12k notch
bn, an = ss.cascade_filters(bn1,an1,bn2,an2)
iir_d.freqz_resp_list([bn],[an],'dB',8000)
grid();
Explanation: Design a cascade of notch filters:
End of explanation
z = signal.lfilter(bn,an,r)
specgram(z,512,8000);
# First save z to a wave file for static playback
ss.to_wav('speech_tone_notch.wav',8000,z)
Audio('speech_tone_notch.wav')
Explanation: Now apply the filter to the composite signal::
End of explanation
Image('images/Flanging_Block.png',width='80%')
Explanation: Adaptive Interference Removal
(Placeholder, but start from ss.lms_ic)
```python
def lms_ic(r,M,mu,delta=1):
    
    Least mean square (LMS) interference canceller adaptive filter.
A complete LMS adaptive filter simulation function for the case of
interference cancellation. Used in the digital filtering case study.
Parameters
----------
M : FIR Filter length (order M-1)
delta : Delay used to generate the reference signal
mu : LMS step-size
delta : decorrelation delay between input and FIR filter input
Returns
-------
n : ndarray Index vector
r : ndarray noisy (with interference) input signal
r_hat : ndarray filtered output (NB_hat[n])
e : ndarray error sequence (WB_hat[n])
ao : ndarray final value of weight vector
F : ndarray frequency response axis vector
Ao : ndarray frequency response of filter
Examples
----------
&gt;&gt;&gt; # import a speech signal
&gt;&gt;&gt; fs,s = from_wav('OSR_us_000_0030_8k.wav')
&gt;&gt;&gt; # add interference at 1kHz and 1.5 kHz and
&gt;&gt;&gt; # truncate to 5 seconds
&gt;&gt;&gt; r = soi_snoi_gen(s,10,5*8000,[1000, 1500])
&gt;&gt;&gt; # simulate with a 64 tap FIR and mu = 0.005
&gt;&gt;&gt; n,r,r_hat,e,ao,F,Ao = lms_ic(r,64,0.005)
```
Audio Special Effects
Consider flanging, which is a combination of a direct signal path and a time varying delay path. In digitalcom there is a function for creating a time-varying delay, $\beta[n]$, that works well for communication systems impairments, but is useful here as well.
Understand that a time varying delay is compressing and expanding the time axis just like the Doppler effect. In music this causes the pitch to wobble, but at slow rates introduces a whooshing effect, made popular in rock music many years ago.
End of explanation
import sk_dsp_comm.digitalcom as dc # for time delay
Audio('c_major.wav')
fs,s12 = ss.from_wav('Music_Test.wav')
fs
# Processing is slow because the time axis interpolation is subsample-based
# Using a 3rd-order Lagrange interpolator
fs,s1 = ss.from_wav('c_major.wav')
#fs,s12 = ss.from_wav('Music_Test.wav')
#s1 = (s12[:,0] + s12[:,0])/2
n = arange(len(s1))
f0 = 1
Dp = 50
D = Dp*(1 + cos(2*pi*f0/fs*n))
x = dc.time_delay(s1,D + 2, 2*Dp+4)
x_wav = x # for PyAudio playback
# Flanged versions of c_major.wav and Music_Test.wav
#ss.to_wav('flanger_audio_c_major.wav',44100,x_wav)
ss.to_wav('flanger_audio_Music_Test.wav',44100,x_wav)
Explanation: The time varying delay in flanging takes the form:
$$
    \beta[n] = D_p \big(1+\cos(2\pi f_0/f_s)\big)
$$
where here $D_p = 50$ and $f_0 \simeq 1$ Hz or less.
Import some sound files and set up the time varying delay.
End of explanation
pah.available_devices()
# define callback
# Here we configure the callback to play back a wav file                      
def callback(in_data, frame_count, time_info, status):
    # Ignore in_data when generating output only
    #***********************************************
    global x
    # Note wav is scaled to [-1,1] so need to rescale to int16
    y = 32767*x.get_samples(frame_count)
    # Save data for later analysis
    # accumulate a new frame of samples
    DSP_IO.DSP_capture_add_samples(y)
    #***********************************************
    # Convert from float back to int16
    y = y.astype(int16)
    return y.tobytes(), pah.pyaudio.paContinue
x = pah.loop_audio(x_wav)
DSP_IO = pah.DSP_io_stream(callback,0,1,fs=44100,Tcapture=2)
DSP_IO.stream(20) # may need to change time
Explanation: Playback using PyAudio (one channel only)
End of explanation
<END_TASK>