Files
OpsLog/internal/audio/mp3.go
T
2026-06-04 00:46:35 +02:00

55 lines
1.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build windows
package audio
import (
"os"
"github.com/braheezy/shine-mp3/pkg/mp3"
)
// mp3Rate is the encode sample rate. The capture pipeline is 16 kHz, but the
// Shine encoder emits broken "free-format" frames at MPEG-2 rates (16/22/24
// kHz) that most players reject. Encoding at an MPEG-1 rate (we upsample ×2 to
// 32 kHz) produces standard, universally-playable MP3s.
const mp3Rate = sampleRate * 2 // 32000
// writeMP3 encodes 16 kHz mono 16-bit PCM to a standard MP3 file using the
// pure-Go Shine encoder (no CGO). Two quirks are worked around:
// - 16 kHz (MPEG-2) yields broken free-format frames → upsample ×2 to 32 kHz.
// - Shine's Write only encodes half the samples for MONO input (its loop
// advances by samples_per_pass*2). Feeding STEREO interleaved data (the
// encoder reads samples_per_pass*channels per pass) encodes everything, so
// we duplicate mono → L=R stereo.
func writeMP3(path string, pcm []byte) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
mono32 := upsample2(bytesToInt16(pcm)) // 16 kHz → 32 kHz mono
stereo := make([]int16, len(mono32)*2) // L=R interleaved
for i, v := range mono32 {
stereo[2*i], stereo[2*i+1] = v, v
}
enc := mp3.NewEncoder(mp3Rate, 2)
return enc.Write(f, stereo)
}
// upsample2 doubles the sample rate with linear interpolation (16 kHz → 32 kHz).
func upsample2(in []int16) []int16 {
if len(in) == 0 {
return in
}
out := make([]int16, len(in)*2)
for i := range in {
out[2*i] = in[i]
if i+1 < len(in) {
out[2*i+1] = int16((int32(in[i]) + int32(in[i+1])) / 2)
} else {
out[2*i+1] = in[i]
}
}
return out
}