66 lines
1.5 KiB
Go
66 lines
1.5 KiB
Go
package output
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"mxl-player/internal/playback"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidAudioFrame = errors.New("invalid audio frame")
|
|
ErrAudioPayloadTooSmall = errors.New("audio channel payload is too small")
|
|
)
|
|
|
|
func InterleaveF32(frame playback.AudioFrame) ([]byte, error) {
|
|
if frame.Channels == 0 ||
|
|
frame.SampleCount == 0 ||
|
|
frame.SampleRateNumerator <= 0 ||
|
|
frame.SampleRateDenominator <= 0 {
|
|
return nil, ErrInvalidAudioFrame
|
|
}
|
|
|
|
if uint64(len(frame.Samples)) != frame.Channels {
|
|
return nil, ErrInvalidAudioFrame
|
|
}
|
|
|
|
if frame.SampleCount > ^uint64(0)/4 {
|
|
return nil, ErrInvalidAudioFrame
|
|
}
|
|
bytesPerChannel := frame.SampleCount * 4
|
|
|
|
if frame.Channels > ^uint64(0)/bytesPerChannel {
|
|
return nil, ErrInvalidAudioFrame
|
|
}
|
|
totalBytes := frame.Channels * bytesPerChannel
|
|
|
|
maxInt := uint64(^uint(0) >> 1)
|
|
if totalBytes > maxInt {
|
|
return nil, ErrInvalidAudioFrame
|
|
}
|
|
|
|
for _, samples := range frame.Samples {
|
|
if bytesPerChannel > uint64(len(samples)) {
|
|
return nil, ErrAudioPayloadTooSmall
|
|
}
|
|
}
|
|
|
|
result := make([]byte, int(totalBytes))
|
|
|
|
for sample := uint64(0); sample < frame.SampleCount; sample++ {
|
|
for channel := uint64(0); channel < frame.Channels; channel++ {
|
|
sourceOffset := sample * 4
|
|
destinationOffset := (sample*frame.Channels + channel) * 4
|
|
|
|
sourceStart := int(sourceOffset)
|
|
destinationStart := int(destinationOffset)
|
|
|
|
copy(
|
|
result[destinationStart:destinationStart+4],
|
|
frame.Samples[channel][sourceStart:sourceStart+4],
|
|
)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|