Initial commit. Complete first version of the deconvolver
This commit is contained in:
189
main.go
Normal file
189
main.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"valhallir-convoluter/pkg/convolve"
|
||||
"valhallir-convoluter/pkg/wav"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "valhallir-convoluter",
|
||||
Usage: "Convolve sweep and recorded WAV files to create impulse responses",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "sweep",
|
||||
Usage: "Path to the sweep WAV file (96kHz 24bit)",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "recorded",
|
||||
Usage: "Path to the recorded WAV file (96kHz 24bit)",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Path to the output IR WAV file (96kHz 24bit)",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "mpt",
|
||||
Usage: "Generate minimum phase transform IR in addition to regular IR",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "sample-rate",
|
||||
Usage: "Output sample rate (44, 48, 88, 96 kHz)",
|
||||
Value: 96000,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "bit-depth",
|
||||
Usage: "Output bit depth (16, 24, 32 bit)",
|
||||
Value: 24,
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "normalize",
|
||||
Usage: "Normalize output to this peak value (0.0-1.0, default 0.95)",
|
||||
Value: 0.95,
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "trim-threshold",
|
||||
Usage: "Silence threshold for trimming (0.0-1.0, default 0.001)",
|
||||
Value: 0.001,
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "length-ms",
|
||||
Usage: "Optional: Output IR length in milliseconds (will trim or zero-pad as needed)",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
// Read sweep WAV file
|
||||
sweepData, err := wav.ReadWAVFile(c.String("sweep"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read recorded WAV file
|
||||
recordedData, err := wav.ReadWAVFile(c.String("recorded"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Sweep: %d samples, %d channels", len(sweepData.PCMData), sweepData.Channels)
|
||||
log.Printf("Recorded: %d samples, %d channels", len(recordedData.PCMData), recordedData.Channels)
|
||||
|
||||
log.Println("Performing deconvolution...")
|
||||
ir := convolve.Deconvolve(sweepData.PCMData, recordedData.PCMData)
|
||||
log.Printf("Deconvolution result: %d samples", len(ir))
|
||||
|
||||
log.Println("Trimming silence...")
|
||||
ir = convolve.TrimSilence(ir, 1e-5)
|
||||
log.Printf("After trimming: %d samples", len(ir))
|
||||
|
||||
log.Println("Normalizing...")
|
||||
ir = convolve.Normalize(ir, c.Float64("normalize"))
|
||||
log.Printf("Final IR: %d samples", len(ir))
|
||||
|
||||
// Validate output format options
|
||||
sampleRate := c.Int("sample-rate")
|
||||
bitDepth := c.Int("bit-depth")
|
||||
|
||||
// Validate sample rate
|
||||
validSampleRates := []int{44100, 48000, 88200, 96000}
|
||||
validSampleRate := false
|
||||
for _, sr := range validSampleRates {
|
||||
if sampleRate == sr {
|
||||
validSampleRate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validSampleRate {
|
||||
return fmt.Errorf("invalid sample rate: %d. Valid options: %v", sampleRate, validSampleRates)
|
||||
}
|
||||
|
||||
// Validate bit depth
|
||||
validBitDepths := []int{16, 24, 32}
|
||||
validBitDepth := false
|
||||
for _, bd := range validBitDepths {
|
||||
if bitDepth == bd {
|
||||
validBitDepth = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validBitDepth {
|
||||
return fmt.Errorf("invalid bit depth: %d. Valid options: %v", bitDepth, validBitDepths)
|
||||
}
|
||||
|
||||
// Resample IR to target sample rate if different from input (96kHz)
|
||||
targetSampleRate := sampleRate
|
||||
if targetSampleRate != 96000 {
|
||||
log.Printf("Resampling IR from 96kHz to %dHz...", targetSampleRate)
|
||||
ir = convolve.Resample(ir, 96000, targetSampleRate)
|
||||
log.Printf("Resampled IR: %d samples", len(ir))
|
||||
}
|
||||
|
||||
// Trim or pad IR to requested length if --length-ms is set
|
||||
lengthMs := c.Float64("length-ms")
|
||||
if lengthMs > 0 {
|
||||
targetSamples := int(float64(targetSampleRate) * lengthMs / 1000.0)
|
||||
log.Printf("Trimming or padding IR to %d samples (%.2f ms)...", targetSamples, lengthMs)
|
||||
ir = convolve.TrimOrPad(ir, targetSamples)
|
||||
}
|
||||
|
||||
// Write regular IR
|
||||
log.Printf("Writing IR to: %s (%dHz, %d-bit WAV)", c.String("output"), sampleRate, bitDepth)
|
||||
if err := wav.WriteWAVFileWithOptions(c.String("output"), ir, sampleRate, bitDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate MPT IR if requested
|
||||
if c.Bool("mpt") {
|
||||
log.Println("Generating minimum phase transform...")
|
||||
// Use the original 96kHz IR for MPT generation
|
||||
originalIR := convolve.Deconvolve(sweepData.PCMData, recordedData.PCMData)
|
||||
originalIR = convolve.TrimSilence(originalIR, 1e-5)
|
||||
mptIR := convolve.MinimumPhaseTransform(originalIR)
|
||||
mptIR = convolve.Normalize(mptIR, c.Float64("normalize"))
|
||||
log.Printf("MPT IR: %d samples", len(mptIR))
|
||||
|
||||
// Resample MPT IR to target sample rate if different from input (96kHz)
|
||||
if targetSampleRate != 96000 {
|
||||
log.Printf("Resampling MPT IR from 96kHz to %dHz...", targetSampleRate)
|
||||
mptIR = convolve.Resample(mptIR, 96000, targetSampleRate)
|
||||
log.Printf("Resampled MPT IR: %d samples", len(mptIR))
|
||||
}
|
||||
|
||||
// Trim or pad MPT IR to requested length if --length-ms is set
|
||||
if lengthMs > 0 {
|
||||
targetSamples := int(float64(targetSampleRate) * lengthMs / 1000.0)
|
||||
log.Printf("Trimming or padding MPT IR to %d samples (%.2f ms)...", targetSamples, lengthMs)
|
||||
mptIR = convolve.TrimOrPad(mptIR, targetSamples)
|
||||
}
|
||||
|
||||
// Generate MPT output filename
|
||||
outputPath := c.String("output")
|
||||
if len(outputPath) > 4 && outputPath[len(outputPath)-4:] == ".wav" {
|
||||
outputPath = outputPath[:len(outputPath)-4]
|
||||
}
|
||||
mptOutputPath := outputPath + "_mpt.wav"
|
||||
|
||||
log.Printf("Writing MPT IR to: %s (%dHz, %d-bit WAV)", mptOutputPath, sampleRate, bitDepth)
|
||||
if err := wav.WriteWAVFileWithOptions(mptOutputPath, mptIR, sampleRate, bitDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("Minimum phase transform IR generated successfully!")
|
||||
}
|
||||
|
||||
log.Println("Impulse response generated successfully!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user