mirror of
https://github.com/cgzirim/seek-tune.git
synced 2025-12-18 09:24:19 +00:00
feat: enhance ReadWavInfo and WavInfo struct to include left and right channel samples
This commit is contained in:
parent
e3ddaa050a
commit
781438c7b8
1 changed files with 51 additions and 17 deletions
|
|
@ -94,49 +94,83 @@ func WriteWavFile(filename string, data []byte, sampleRate int, channels int, bi
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WavInfo defines a struct containing information extracted from the WAV header
|
|
||||||
type WavInfo struct {
|
type WavInfo struct {
|
||||||
Channels int
|
Channels int
|
||||||
SampleRate int
|
SampleRate int
|
||||||
Data []byte
|
Duration float64
|
||||||
Duration float64
|
Data []byte
|
||||||
|
LeftChannelSamples []float64
|
||||||
|
RightChannelSamples []float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadWavInfo reads a 16-bit PCM WAV file and returns its metadata and audio samples.
|
||||||
|
// Supports mono and stereo files. Note that it only supports 16-bit PCM format.
|
||||||
func ReadWavInfo(filename string) (*WavInfo, error) {
|
func ReadWavInfo(filename string) (*WavInfo, error) {
|
||||||
data, err := ioutil.ReadFile(filename)
|
data, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) < 44 {
|
if len(data) < 44 {
|
||||||
return nil, errors.New("invalid WAV file size (too small)")
|
return nil, errors.New("invalid WAV file size (too small)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read header chunks
|
// Parse PCM header to extract metadata
|
||||||
|
// https://en.wikipedia.org/wiki/WAV#WAV_file_header
|
||||||
var header WavHeader
|
var header WavHeader
|
||||||
err = binary.Read(bytes.NewReader(data[:44]), binary.LittleEndian, &header)
|
if err := binary.Read(bytes.NewReader(data[:44]), binary.LittleEndian, &header); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if string(header.ChunkID[:]) != "RIFF" ||
|
||||||
if string(header.ChunkID[:]) != "RIFF" || string(header.Format[:]) != "WAVE" || header.AudioFormat != 1 {
|
string(header.Format[:]) != "WAVE" ||
|
||||||
|
header.AudioFormat != 1 {
|
||||||
return nil, errors.New("invalid WAV header format")
|
return nil, errors.New("invalid WAV header format")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract information
|
|
||||||
info := &WavInfo{
|
info := &WavInfo{
|
||||||
Channels: int(header.NumChannels),
|
Channels: int(header.NumChannels),
|
||||||
SampleRate: int(header.SampleRate),
|
SampleRate: int(header.SampleRate),
|
||||||
Data: data[44:],
|
Data: data[44:],
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate audio duration (assuming data contains PCM data)
|
if header.BitsPerSample != 16 {
|
||||||
if header.BitsPerSample == 16 {
|
return nil, errors.New("unsupported bits‑per‑sample (expect 16‑bit PCM)")
|
||||||
info.Duration = float64(len(info.Data)) / float64(int(header.NumChannels)*2*int(header.SampleRate))
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("unsupported bits per sample format")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sampleCount := len(info.Data) / 2
|
||||||
|
int16Buf := make([]int16, sampleCount)
|
||||||
|
if err := binary.Read(bytes.NewReader(info.Data), binary.LittleEndian, int16Buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const scale = 1.0 / 32768.0 // 16‑bit normalisation factor
|
||||||
|
|
||||||
|
switch header.NumChannels {
|
||||||
|
case 1:
|
||||||
|
left := make([]float64, sampleCount)
|
||||||
|
for i, s := range int16Buf {
|
||||||
|
left[i] = float64(s) * scale
|
||||||
|
}
|
||||||
|
info.LeftChannelSamples = left
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
frameCount := sampleCount / 2
|
||||||
|
left := make([]float64, frameCount)
|
||||||
|
right := make([]float64, frameCount)
|
||||||
|
for i := 0; i < frameCount; i++ {
|
||||||
|
left[i] = float64(int16Buf[2*i]) * scale
|
||||||
|
right[i] = float64(int16Buf[2*i+1]) * scale
|
||||||
|
}
|
||||||
|
info.LeftChannelSamples = left
|
||||||
|
info.RightChannelSamples = right
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported channel count (only mono/stereo)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute audio duration in seconds
|
||||||
|
info.Duration = float64(sampleCount) /
|
||||||
|
(float64(header.NumChannels) * float64(header.SampleRate))
|
||||||
|
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue