53 lines
1.6 KiB
Go
53 lines
1.6 KiB
Go
package ai
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"math"
|
||
|
||
"git.ipao.vip/rogee/go-sip/internal/audio"
|
||
)
|
||
|
||
const (
|
||
internalPCM16SampleRate = 16000
|
||
minASRSampleRate = 8000
|
||
maxASRSampleRate = 48000
|
||
)
|
||
|
||
func parseASRSampleRate(params map[string]json.RawMessage) (int, error) {
|
||
raw, ok := params["sample_rate"]
|
||
if !ok {
|
||
return 0, errors.New("ASR params.sample_rate is required")
|
||
}
|
||
return parseSampleRateValue(raw, "ASR params.sample_rate")
|
||
}
|
||
|
||
func parseSampleRateValue(raw json.RawMessage, field string) (int, error) {
|
||
var rate float64
|
||
if err := json.Unmarshal(raw, &rate); err != nil {
|
||
return 0, fmt.Errorf("%s must be numeric", field)
|
||
}
|
||
if math.IsNaN(rate) || math.IsInf(rate, 0) || math.Trunc(rate) != rate || rate < minASRSampleRate || rate > maxASRSampleRate {
|
||
return 0, fmt.Errorf("%s must be an integer from 8000 to 48000 Hz", field)
|
||
}
|
||
return int(rate), nil
|
||
}
|
||
|
||
func prepareASRPCM16(pcm16 []byte, targetRate int) ([]byte, error) {
|
||
if len(pcm16) == 0 || len(pcm16)%2 != 0 {
|
||
return nil, errors.New("ASR input must contain nonempty signed PCM16 samples")
|
||
}
|
||
if targetRate < minASRSampleRate || targetRate > maxASRSampleRate {
|
||
return nil, errors.New("ASR target sample rate is outside the supported 8000–48000 Hz range")
|
||
}
|
||
|
||
// The media layer uses the same resampler when converting the supported
|
||
// 8-kHz PCMA wire audio to the agent's 16-kHz PCM16 format.
|
||
resampled := audio.ResamplePCM16(pcm16, internalPCM16SampleRate, targetRate)
|
||
if len(resampled) == 0 || len(resampled)%2 != 0 {
|
||
return nil, errors.New("ASR resampling produced no complete PCM16 samples")
|
||
}
|
||
return resampled, nil
|
||
}
|