package synth import ( "encoding/binary" "math" ) // PanGains returns left and right channel gains for a pan position p in [-1, 1]. // Uses constant-power (equal-power) pan law: cos/sin mapping. // Per D-11: bass frequencies center, mid spread L/R, higher frequencies wider. // At p=-1.0: gainL=1.0, gainR=0.0; at p=0.0: gainL=gainR=sqrt(2)/2; at p=1.0: gainL=0.0, gainR=1.0. func PanGains(p float64) (gainL, gainR float64) { angle := (p + 1.0) / 2.0 * math.Pi / 2.0 return math.Cos(angle), math.Sin(angle) } // StereoFramesToInt16Bytes converts [][2]float64 stereo frames to interleaved // little-endian int16 bytes suitable for go-lame's Write method. // Clamps values to [-1.0, 1.0] before conversion. // Output format: [L0_lo, L0_hi, R0_lo, R0_hi, L1_lo, L1_hi, R1_lo, R1_hi, ...] func StereoFramesToInt16Bytes(frames [][2]float64) []byte { buf := make([]byte, len(frames)*4) // 2 channels * 2 bytes per sample for i, frame := range frames { l := clamp(frame[0]) r := clamp(frame[1]) binary.LittleEndian.PutUint16(buf[i*4:], uint16(int16(l*32767))) binary.LittleEndian.PutUint16(buf[i*4+2:], uint16(int16(r*32767))) } return buf } func clamp(v float64) float64 { if v > 1.0 { return 1.0 } if v < -1.0 { return -1.0 } return v }