Add cabpack generation with default mode and file organization

This commit is contained in:
Bastian Bührig
2025-12-02 14:28:39 +01:00
parent 1954312833
commit 73cff7ea08
4 changed files with 1076 additions and 171 deletions

138
README.md
View File

@@ -16,6 +16,8 @@ A CLI tool for processing WAV files to generate impulse responses (IR) from swee
- **Multiple output formats** with configurable sample rates and bit depths
- **Minimum Phase Transform (MPT)** option for reduced latency IRs
- **Automatic silence trimming** and normalization
- **Batch processing:** Process entire directories of recorded files automatically
- **Cabpack generation:** Create complete cabpack structures with IRs in multiple formats organized in a directory tree
- **Modular design** with separate packages for WAV I/O, convolution, and visualization
- **Robust error handling** and validation
@@ -32,6 +34,24 @@ go build -o valhallir-deconvolver
## Usage
### Default Mode (No Options)
If you run the tool without any command-line options, it automatically creates a cabpack:
```sh
./valhallir-deconvolver
```
This will:
- Use the current directory as the recorded directory
- Look for `sweep.wav` in the current directory as the sweep file
- Create output in an `IRs` folder one directory level up from the current directory
- Automatically enable cabpack mode
- Process all WAV files in the current directory (excluding the sweep file)
- Use `selection.txt` and `ambient.txt` from the current directory if present
**Perfect for:** Simply placing the tool in a folder with recorded WAV files and running it to create a complete cabpack in the parent directory's `IRs` folder.
### Basic IR Generation
Generate a standard impulse response from sweep and recorded files (any WAV format):
@@ -40,6 +60,105 @@ Generate a standard impulse response from sweep and recorded files (any WAV form
./valhallir-deconvolver --sweep sweep.wav --recorded recorded.wav --output ir.wav
```
### Batch Processing (Directory Mode)
Process all WAV files in a directory of recorded files. The tool will automatically detect if `--recorded` is a directory and process all WAV files found in it (including subdirectories):
```sh
./valhallir-deconvolver --sweep sweep.wav --recorded ./recordings/ --output ./ir_output/
```
This will:
- Find all `.wav` files in the `./recordings/` directory (recursively)
- Process each recorded file with the same sweep file
- Generate IR files in the `./ir_output/` directory
- Use the original recorded filename for each output IR (e.g., `recorded1.wav``recorded1.wav`)
**Note:** If `--output` is a directory, it will be created automatically if it doesn't exist. If `--output` is a file path, single-file mode is used.
**Example with options:**
```sh
./valhallir-deconvolver \
--sweep sweep.wav \
--recorded ./recordings/ \
--output ./ir_output/ \
--mpt \
--length-ms 50 \
--sample-rate 48000 \
--bit-depth 24
```
This processes all WAV files in `./recordings/` and generates both regular and MPT IRs in `./ir_output/`.
### Cabpack Generation
Generate a complete cabpack with IRs in multiple formats organized in a structured directory tree:
**Simple mode (default - no options needed):**
```sh
./valhallir-deconvolver
```
This uses:
- Current directory as recorded directory
- `sweep.wav` in current directory as sweep file
- `IRs` folder one directory level up from current directory as output
- Automatically enables cabpack mode
- Excludes the sweep file from processing
**Explicit mode:**
```sh
./valhallir-deconvolver \
--sweep sweep.wav \
--recorded ./recordings/ \
--output ./cabpack_output/ \
--cabpack
```
Both modes create a cabpack structure with:
- **Format folders** named like `V2-1960STV 96000Hz-24bit 500ms` (extracted from WAV filenames)
- **9 different formats** covering various sample rates, bit depths, and lengths:
- 44100Hz, 16bit, 170ms
- 44100Hz, 24bit, 170ms
- 44100Hz, 24bit, 500ms
- 48000Hz, 16bit, 170ms
- 48000Hz, 24bit, 170ms
- 48000Hz, 24bit, 500ms
- 48000Hz, 24bit, 1370ms
- 96000Hz, 24bit, 500ms
- 96000Hz, 24bit, 1370ms
- **Subfolders** in each format: `ambient mics`, `close mics`, `mixees`, `selection`
- **RAW and MPT IRs** in `close mics/RAW` and `close mics/MPT` respectively
- **IR visualizations** in the `plots` folder (96000Hz format)
- **Automatic file organization** using `selection.txt` and `ambient.txt` files
The cabpack base name is automatically extracted from WAV filenames. For example, `V2-1960STV-d-SM7B-A1.wav` will create a cabpack named `V2-1960STV`.
#### Automatic File Organization
You can automatically populate the `selection` and `ambient mics` folders by placing text files in the recorded directory:
- **`selection.txt`**: Lists filenames (one per line) to **copy** from `close mics` to `selection` folders. Both RAW and MPT versions are copied.
- **`ambient.txt`**: Lists filenames (one per line) to **move** from `close mics` to `ambient mics` folders. Both RAW and MPT versions are moved.
**Example `selection.txt`:**
```
V2-1960STV-d-SM7B-A1.wav
V2-1960STV-d-SM7B-A2.wav
V2-1960STV-d-SM7B-A3.wav
```
**Example `ambient.txt`:**
```
V2-1960STV-d-SM7B-B1.wav
V2-1960STV-d-SM7B-B2.wav
```
**Notes:**
- If `selection.txt` or `ambient.txt` are missing, those operations are skipped (no error).
- Files are processed for all format folders automatically.
- The `.wav` extension is automatically added if missing in the list files.
- The `mixees` folder is left empty for manual filling.
### With Minimum Phase Transform
Generate both regular and minimum phase IRs:
@@ -183,9 +302,9 @@ Generate IRs in different sample rates and bit depths:
| Flag | Description | Default | Required |
|------|-------------|---------|----------|
| `--sweep` | Path to sweep WAV file (any format) | - | Yes |
| `--recorded` | Path to recorded WAV file (any format) | - | Yes |
| `--output` | Path to output IR WAV file | - | Yes |
| `--sweep` | Path to sweep WAV file (any format) | `sweep.wav` in current directory | No |
| `--recorded` | Path to recorded WAV file or directory containing WAV files | Current directory | No |
| `--output` | Path to output IR WAV file or directory for batch processing | `IRs` folder one directory level up from recorded directory | No |
| `--mpt` | Generate minimum phase transform IR | false | No |
| `--sample-rate` | Output sample rate (44, 48, 88, 96 kHz) | 96000 | No |
| `--bit-depth` | Output bit depth (16, 24, 32 bit) | 24 | No |
@@ -199,6 +318,7 @@ Generate IRs in different sample rates and bit depths:
| `--plot-ir` | Generate frequency response and waveform plot | false | No |
| `--no-phase-correction` | Disable automatic phase correction | false | No |
| `--force-invert-phase` | Force inversion of the recorded sweep (manual override) | false | No |
| `--cabpack` | Generate a cabpack with IRs in multiple formats organized in a directory tree | false | No |
## File Requirements
@@ -311,6 +431,18 @@ Generate IRs in different sample rates and bit depths:
--mpt
```
### Cabpack Generation
```sh
# Generate a complete cabpack with all formats
./valhallir-deconvolver \
--sweep sweep.wav \
--recorded ./recordings/ \
--output ./cabpack_output/ \
--cabpack
```
This will process all WAV files in `./recordings/` and create a complete cabpack structure with IRs in 9 different formats, organized in folders with RAW and MPT versions, plus visualization plots.
## CI/CD & Multi-Platform Builds
This project includes a Dagger pipeline for building binaries for multiple platforms:

1095
main.go

File diff suppressed because it is too large Load Diff

View File

@@ -62,8 +62,9 @@ func PlotIR(ir []float64, sampleRate int, irFileName string) error {
}
}
fmt.Printf("[PlotIR] minDb in plotted range: %.2f dB at %.2f Hz\n", minDb, minDbFreq)
irBaseName := filepath.Base(irFileName)
p := plot.New()
p.Title.Text = "IR Frequency Response (dB, 2048-sample window)"
p.Title.Text = fmt.Sprintf("IR Frequency Response: %s", irBaseName)
p.X.Label.Text = "Frequency (Hz)"
p.Y.Label.Text = "Magnitude (dB)"
p.X.Scale = plot.LogScale{}
@@ -111,7 +112,7 @@ func PlotIR(ir []float64, sampleRate int, irFileName string) error {
// --- Time-aligned waveform plot ---
p2 := plot.New()
p2.Title.Text = "IR Waveform (Time Aligned)"
p2.Title.Text = fmt.Sprintf("IR Waveform: %s", irBaseName)
p2.X.Label.Text = "Time (ms)"
p2.Y.Label.Text = "Amplitude"
// Prepare waveform data (only first 10ms)

View File

@@ -36,6 +36,15 @@ func toMono(data []float64, channels int) []float64 {
// ReadWAVFile reads a WAV file and returns its PCM data as float64 (resampled to 96kHz mono)
func ReadWAVFile(filePath string) (*WAVData, error) {
// Check if path is a directory
info, err := os.Stat(filePath)
if err != nil {
return nil, fmt.Errorf("failed to access file %s: %w", filePath, err)
}
if info.IsDir() {
return nil, fmt.Errorf("path %s is a directory, not a WAV file", filePath)
}
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("failed to open file %s: %w", filePath, err)