A simple limiter
October 11, 2015 ยท View on GitHub
from sounddevice import Stream, CallbackStop from time import sleep from numpy import array, random, zeros import matplotlib.pyplot as plt
################################### Constants ##################################
fs = 44100 # Hz threshold = 0.6 # absolute gain delay = 40 # samples signal_length = 1 # second release_coeff = 0.9995 # release time factor attack_coeff = 0.9 # attack time factor block_length = 1024 # samples
#################### Generate quiet-loud-quiet noise signal ####################
signal = array(random.rand(fssignal_length)2-1) signal[:signal_lengthfs/3] = 0.1 signal[signal_lengthfs2/3:] *= 0.1
############################# Implementation of Limiter ########################
class Limiter: def init(self, attack_coeff, release_coeff, delay, threshold): self.delay_index = 0 self.envelope = 0 self.gain = 1 self.delay = delay self.delay_line = zeros(delay) self.release_coeff = release_coeff self.attack_coeff = attack_coeff self.threshold = threshold
def limit(self, signal):
for idx, sample in enumerate(signal):
self.delay_line[self.delay_index] = sample
self.delay_index = (self.delay_index + 1) % self.delay
# calculate an envelope of the signal
self.envelope = max(abs(sample), self.envelope*self.release_coeff)
if self.envelope > self.threshold:
target_gain = self.threshold / self.envelope
else:
target_gain = 1.0
# have self.gain go towards a desired limiter gain
self.gain = ( self.gain*self.attack_coeff +
target_gain*(1-self.attack_coeff) )
# limit the delayed signal
signal[idx] = self.delay_line[self.delay_index] * self.gain
return signal
################################# Play the Audio ###############################
original_signal = array(signal, copy=True) output_signal = array(signal, copy=True)
limiter = Limiter(attack_coeff, release_coeff, delay, threshold)
def callback(indata, outdata, frames, time, status): if status: print("Playback Error: {}".format(status)) played_frames = callback.counter callback.counter += frames if callback.counter > len(signal): raise CallbackStop() limited_signal = limiter.limit(signal[played_frames:callback.counter]) outdata[:, 0] = limited_signal output_signal[played_frames:callback.counter] = limited_signal
callback.counter = 0
with Stream(channels=1, callback=callback) as s: while s.active: sleep(0.1)
############################## Plot results ####################################
plt.figure() plt.plot(original_signal, color='grey', label='original signal') plt.plot(output_signal, color='black', label='limited signal') plt.legend() plt.show(block=True)