Initial commit. Complete first version of the deconvolver
This commit is contained in:
104
pkg/wav/reader.go
Normal file
104
pkg/wav/reader.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package wav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"valhallir-convoluter/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) {
|
||||
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
|
||||
}
|
||||
90
pkg/wav/writer.go
Normal file
90
pkg/wav/writer.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package wav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/go-audio/audio"
|
||||
"github.com/go-audio/wav"
|
||||
)
|
||||
|
||||
// WriteWAVFileWithOptions writes float64 audio data to a WAV file with specified sample rate and bit depth
|
||||
func WriteWAVFileWithOptions(filePath string, data []float64, sampleRate, bitDepth int) error {
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file %s: %w", filePath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Convert float64 to appropriate integer format based on bit depth
|
||||
var intData []int
|
||||
switch bitDepth {
|
||||
case 16:
|
||||
intData = make([]int, len(data))
|
||||
for i, sample := range data {
|
||||
// Clamp to [-1, 1] range
|
||||
if sample > 1.0 {
|
||||
sample = 1.0
|
||||
} else if sample < -1.0 {
|
||||
sample = -1.0
|
||||
}
|
||||
// Convert to 16-bit integer
|
||||
intSample := int(sample * float64(1<<15))
|
||||
intData[i] = intSample
|
||||
}
|
||||
case 24:
|
||||
intData = make([]int, len(data))
|
||||
for i, sample := range data {
|
||||
// Clamp to [-1, 1] range
|
||||
if sample > 1.0 {
|
||||
sample = 1.0
|
||||
} else if sample < -1.0 {
|
||||
sample = -1.0
|
||||
}
|
||||
// Convert to 24-bit integer
|
||||
intSample := int(sample * float64(1<<23))
|
||||
intData[i] = intSample
|
||||
}
|
||||
case 32:
|
||||
intData = make([]int, len(data))
|
||||
for i, sample := range data {
|
||||
// Clamp to [-1, 1] range
|
||||
if sample > 1.0 {
|
||||
sample = 1.0
|
||||
} else if sample < -1.0 {
|
||||
sample = -1.0
|
||||
}
|
||||
// Convert to 32-bit integer
|
||||
intSample := int(sample * float64(1<<31))
|
||||
intData[i] = intSample
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported bit depth: %d", bitDepth)
|
||||
}
|
||||
|
||||
// Create audio buffer
|
||||
audioBuf := &audio.IntBuffer{
|
||||
Format: &audio.Format{
|
||||
NumChannels: 1,
|
||||
SampleRate: sampleRate,
|
||||
},
|
||||
Data: intData,
|
||||
SourceBitDepth: bitDepth,
|
||||
}
|
||||
|
||||
// Create WAV encoder
|
||||
encoder := wav.NewEncoder(file, sampleRate, bitDepth, 1, 1)
|
||||
defer encoder.Close()
|
||||
|
||||
// Write audio data
|
||||
if err := encoder.Write(audioBuf); err != nil {
|
||||
return fmt.Errorf("failed to write audio data: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteWAVFile writes float64 audio data to a 96kHz 24-bit WAV file (default format)
|
||||
func WriteWAVFile(filePath string, data []float64, sampleRate int) error {
|
||||
return WriteWAVFileWithOptions(filePath, data, sampleRate, 24)
|
||||
}
|
||||
Reference in New Issue
Block a user