mirror of
https://github.com/cgzirim/seek-tune.git
synced 2025-12-17 17:04:22 +00:00
Create wav.go
This commit is contained in:
parent
845f43b5bf
commit
9c9156c6a4
2 changed files with 170 additions and 0 deletions
37
shazam/filter.go
Normal file
37
shazam/filter.go
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package shazam
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LowPassFilter implements a simple first-order low-pass filter
|
||||||
|
type LowPassFilter struct {
|
||||||
|
alpha float64 // Filter coefficient
|
||||||
|
yPrev float64 // Previous output value
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLowPassFilter creates a new LowPassFilter with the specified cutoff frequency and sample rate
|
||||||
|
func NewLowPassFilter(cutoffFrequency, sampleRate float64) *LowPassFilter {
|
||||||
|
// Calculate filter coefficient (alpha) based on cutoff frequency and sample rate
|
||||||
|
alpha := 1.0 - math.Exp(-2.0*math.Pi*cutoffFrequency/sampleRate)
|
||||||
|
|
||||||
|
return &LowPassFilter{
|
||||||
|
alpha: alpha,
|
||||||
|
yPrev: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter filters the input signal using the low-pass filter and returns the filtered output
|
||||||
|
func (lpf *LowPassFilter) Filter(input []float64) []float64 {
|
||||||
|
filtered := make([]float64, len(input))
|
||||||
|
|
||||||
|
for i, x := range input {
|
||||||
|
// Update filter output using the single-pole low-pass filter equation
|
||||||
|
output := lpf.alpha*x + (1-lpf.alpha)*lpf.yPrev
|
||||||
|
lpf.yPrev = output
|
||||||
|
|
||||||
|
filtered[i] = output
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
133
utils/wav.go
Normal file
133
utils/wav.go
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WavHeader defines the structure of a WAV header
|
||||||
|
type WavHeader struct {
|
||||||
|
ChunkID [4]byte
|
||||||
|
ChunkSize uint32
|
||||||
|
Format [4]byte
|
||||||
|
Subchunk1ID [4]byte
|
||||||
|
Subchunk1Size uint32
|
||||||
|
AudioFormat uint16
|
||||||
|
NumChannels uint16
|
||||||
|
SampleRate uint32
|
||||||
|
BytesPerSec uint32
|
||||||
|
BlockAlign uint16
|
||||||
|
BitsPerSample uint16
|
||||||
|
Subchunk2ID [4]byte
|
||||||
|
Subchunk2Size uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeWavHeader(f *os.File, data []byte, sampleRate int, channels int, bitsPerSample int) error {
|
||||||
|
// Validate input
|
||||||
|
if len(data)%channels != 0 {
|
||||||
|
return errors.New("data size not divisible by channels")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate derived values
|
||||||
|
subchunk1Size := uint32(16) // Assuming PCM format
|
||||||
|
bytesPerSample := bitsPerSample / 8
|
||||||
|
blockAlign := uint16(channels * bytesPerSample)
|
||||||
|
subchunk2Size := uint32(len(data))
|
||||||
|
|
||||||
|
// Build WAV header
|
||||||
|
header := WavHeader{
|
||||||
|
ChunkID: [4]byte{'R', 'I', 'F', 'F'},
|
||||||
|
ChunkSize: uint32(36 + len(data)),
|
||||||
|
Format: [4]byte{'W', 'A', 'V', 'E'},
|
||||||
|
Subchunk1ID: [4]byte{'f', 'm', 't', ' '},
|
||||||
|
Subchunk1Size: subchunk1Size,
|
||||||
|
AudioFormat: uint16(1), // PCM format
|
||||||
|
NumChannels: uint16(channels),
|
||||||
|
SampleRate: uint32(sampleRate),
|
||||||
|
BytesPerSec: uint32(sampleRate * channels * bytesPerSample),
|
||||||
|
BlockAlign: blockAlign,
|
||||||
|
BitsPerSample: uint16(bitsPerSample),
|
||||||
|
Subchunk2ID: [4]byte{'d', 'a', 't', 'a'},
|
||||||
|
Subchunk2Size: subchunk2Size,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write header to file
|
||||||
|
err := binary.Write(f, binary.LittleEndian, header)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteWavFile(filename string, data []byte, sampleRate int, channels int, bitsPerSample int) error {
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
err = writeWavHeader(f, data, sampleRate, channels, bitsPerSample)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WavInfo defines a struct containing information extracted from the WAV header
|
||||||
|
type WavInfo struct {
|
||||||
|
Channels int
|
||||||
|
SampleRate int
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadWavInfo(filename string) (*WavInfo, error) {
|
||||||
|
data, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) < 44 {
|
||||||
|
return nil, errors.New("invalid WAV file size (too small)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read header chunks
|
||||||
|
var header WavHeader
|
||||||
|
err = binary.Read(bytes.NewReader(data[:44]), binary.LittleEndian, &header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate header
|
||||||
|
if string(header.ChunkID[:]) != "RIFF" || string(header.Format[:]) != "WAVE" || header.AudioFormat != 1 {
|
||||||
|
return nil, errors.New("invalid WAV header format")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract information
|
||||||
|
return &WavInfo{
|
||||||
|
Channels: int(header.NumChannels),
|
||||||
|
SampleRate: int(header.SampleRate),
|
||||||
|
Data: data[44:],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WavBytesToFloat64 converts a slice of bytes from a .wav file to a slice of float64 samples
|
||||||
|
func WavBytesToFloat64(input []byte) ([]float64, error) {
|
||||||
|
if len(input)%2 != 0 {
|
||||||
|
return nil, errors.New("invalid input length")
|
||||||
|
}
|
||||||
|
|
||||||
|
numSamples := len(input) / 2
|
||||||
|
output := make([]float64, numSamples)
|
||||||
|
|
||||||
|
for i := 0; i < len(input); i += 2 {
|
||||||
|
// Interpret bytes as a 16-bit signed integer (little-endian)
|
||||||
|
sample := int16(binary.LittleEndian.Uint16(input[i : i+2]))
|
||||||
|
|
||||||
|
// Scale the sample to the range [-1, 1]
|
||||||
|
output[i/2] = float64(sample) / 32768.0
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue