Skip to content

Instantly share code, notes, and snippets.

@ravarcheon
Created March 6, 2026 02:01
Show Gist options
  • Select an option

  • Save ravarcheon/28d48aac767930cecd3dc536f1576581 to your computer and use it in GitHub Desktop.

Select an option

Save ravarcheon/28d48aac767930cecd3dc536f1576581 to your computer and use it in GitHub Desktop.
makes a minimum phase version of the input audio
import numpy as np
import soundfile as sf
import os
import tkinter as tk
from tkinter import filedialog
from scipy.fft import fft, ifft, next_fast_len
def minipassMethod(ir):
ir = np.asarray(ir, dtype=np.float64)
N = len(ir)
L = next_fast_len(16 * N) # zero pad to efficient length, anything above 2*N pretty much. the bigger, the less artifacts you get
# should probably actually find a better purer way of doing this, im unsure if its possible to actually get a perfect minimized phase at all but im hoping somewhere out there in the vast realm of signal processing that there is some sure-fire way of actually minning the phase completely without any artifacts or ringing or reflections or whateverhaveyou within a reasonable time complexity and memory complexity hopefully at most nlogn on both (math crack pipe dream honestly)
# god i really don't know what im doing
print("fft")
spectrum = fft(ir, n=L) # fft of padded signal
mag = np.abs(spectrum)
mag = np.maximum(mag, np.finfo(np.float64).tiny)
print("cep")
cepstrum = ifft(np.log(mag)).real # i think its important to remember that numpy does the natural log. i keep forgetting this over time lmaooaoa
print("min")
minphaseCep = np.zeros_like(cepstrum)
minphaseCep[0] = cepstrum[0]
# this odd even check will pretty much always be even unless you decide change that at the definition of L
if L % 2 == 0:
minphaseCep[1:L // 2] = 2 * cepstrum[1:L // 2]
minphaseCep[L // 2] = cepstrum[L // 2] # no nyquist change
else:
minphaseCep[1:(L + 1) // 2] = 2 * cepstrum[1:(L + 1) // 2]
print("recon")
minphaseSpectrum = np.exp(fft(minphaseCep))
minphaseIR = ifft(minphaseSpectrum).real[:N]
return minphaseIR
def processAudio(input, out):
data, sr = sf.read(input, always_2d=True)
minphaseSignal = np.stack([minipassMethod(ch) for ch in data.T], axis=1)
sf.write(out, minphaseSignal, sr, subtype='FLOAT')
if __name__ == "__main__":
root = tk.Tk()
root.withdraw()
filePath = filedialog.askopenfilename(filetypes=[("Audio files", "*.wav *.flac *.aiff *.ogg *.mp3 *.m4a *.opus")])
if filePath:
dirName, baseName = os.path.split(filePath)
baseName = os.path.splitext(baseName)[0]
outName = f"minphase {baseName}.wav"
outPath = os.path.join(dirName, outName)
processAudio(filePath, outPath)
print(f"minphase@: {outPath}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment