114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
package wav
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"valhallir-deconvolver/pkg/convolve"
|
|
|
|
"github.com/go-audio/audio"
|
|
"github.com/go-audio/wav"
|
|
)
|
|
|
|
// WAVData represents the PCM data and metadata from a WAV file
|
|
type WAVData struct {
|
|
SampleRate int
|
|
BitDepth int
|
|
Channels int
|
|
PCMData []float64
|
|
}
|
|
|
|
// toMono averages all channels to mono
|
|
func toMono(data []float64, channels int) []float64 {
|
|
if channels == 1 {
|
|
return data
|
|
}
|
|
mono := make([]float64, len(data)/channels)
|
|
for i := 0; i < len(mono); i++ {
|
|
sum := 0.0
|
|
for c := 0; c < channels; c++ {
|
|
sum += data[i*channels+c]
|
|
}
|
|
mono[i] = sum / float64(channels)
|
|
}
|
|
return mono
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
defer file.Close()
|
|
|
|
decoder := wav.NewDecoder(file)
|
|
if !decoder.IsValidFile() {
|
|
return nil, fmt.Errorf("file %s is not a valid WAV file", filePath)
|
|
}
|
|
|
|
// Read all PCM data
|
|
var pcmData []int32
|
|
buf := &audio.IntBuffer{Data: make([]int, 4096), Format: &audio.Format{SampleRate: int(decoder.SampleRate), NumChannels: int(decoder.NumChans)}}
|
|
|
|
for {
|
|
n, err := decoder.PCMBuffer(buf)
|
|
if err != nil {
|
|
break
|
|
}
|
|
if n == 0 {
|
|
break
|
|
}
|
|
|
|
// Convert int samples to float64
|
|
for i := 0; i < n; i++ {
|
|
pcmData = append(pcmData, int32(buf.Data[i]))
|
|
}
|
|
}
|
|
|
|
// Convert int32 to float64 (-1.0 to 1.0 range, scale by bit depth)
|
|
floatData := make([]float64, len(pcmData))
|
|
var norm float64
|
|
if decoder.BitDepth == 16 {
|
|
norm = float64(1 << 15)
|
|
} else if decoder.BitDepth == 24 {
|
|
norm = float64(1 << 23)
|
|
} else if decoder.BitDepth == 32 {
|
|
norm = float64(1 << 31)
|
|
} else {
|
|
norm = float64(1 << 23) // fallback
|
|
}
|
|
for i, sample := range pcmData {
|
|
floatData[i] = float64(sample) / norm
|
|
}
|
|
|
|
// Convert to mono if needed
|
|
channels := int(decoder.NumChans)
|
|
if channels > 1 {
|
|
floatData = toMono(floatData, channels)
|
|
channels = 1
|
|
}
|
|
|
|
// Resample to 96kHz if needed
|
|
inSampleRate := int(decoder.SampleRate)
|
|
if inSampleRate != 96000 {
|
|
floatData = convolve.Resample(floatData, inSampleRate, 96000)
|
|
}
|
|
|
|
return &WAVData{
|
|
SampleRate: 96000,
|
|
BitDepth: int(decoder.BitDepth), // original bit depth
|
|
Channels: 1,
|
|
PCMData: floatData,
|
|
}, nil
|
|
}
|