audio/internal/convert: bug fix: a returned stream's Length and actual read buffer length didn't match

Closes #3352
This commit is contained in:
Hajime Hoshi
2025-12-01 15:18:33 +09:00
parent 3e2866c664
commit 8417dc2c98
3 changed files with 42 additions and 14 deletions

View File

@@ -556,7 +556,7 @@ func (h *hookerImpl) AppendHookOnBeforeUpdate(f func() error) {
}
// ResampleReader converts the sample rate of the given singed 16bit integer, little-endian, 2 channels (stereo) stream.
// size is the length of the source stream in bytes.
// size is the length of the source stream in bytes. 0 indicates the length is unknown.
// from is the original sample rate.
// to is the target sample rate.
//
@@ -573,7 +573,7 @@ func ResampleReader(source io.Reader, size int64, from, to int) io.Reader {
}
// ResampleReaderF32 converts the sample rate of the given 32bit float, little-endian, 2 channels (stereo) stream.
// size is the length of the source stream in bytes.
// size is the length of the source stream in bytes. 0 indicates the length is unknown.
// from is the original sample rate.
// to is the target sample rate.
//
@@ -590,7 +590,7 @@ func ResampleReaderF32(source io.Reader, size int64, from, to int) io.Reader {
}
// Resample converts the sample rate of the given singed 16bit integer, little-endian, 2 channels (stereo) stream.
// size is the length of the source stream in bytes.
// size is the length of the source stream in bytes. 0 indicates the length is unknown.
// from is the original sample rate.
// to is the target sample rate.
//
@@ -605,7 +605,7 @@ func Resample(source io.ReadSeeker, size int64, from, to int) io.ReadSeeker {
}
// ResampleF32 converts the sample rate of the given 32bit float, little-endian, 2 channels (stereo) stream.
// size is the length of the source stream in bytes.
// size is the length of the source stream in bytes. 0 indicates the length is unknown.
// from is the original sample rate.
// to is the target sample rate.
//

View File

@@ -80,8 +80,11 @@ func sinc01(x float64) float64 {
}
type Resampling struct {
source io.Reader
size int64
source io.Reader
// size is the length of the source stream in bytes. 0 indicates the length is unknown.
size int64
from int
to int
bitDepthInBytes int
@@ -199,10 +202,8 @@ func (r *Resampling) src(i int64) (float64, float64, error) {
if r.eofBufIndex == r.srcBlock && ii >= int64(len(r.srcBufL[r.srcBlock])-1) {
err = io.EOF
}
if _, ok := r.source.(io.Seeker); ok {
if r.size/sizePerSample <= i {
err = io.EOF
}
if r.size > 0 && r.size/sizePerSample <= i {
err = io.EOF
}
return r.srcBufL[r.srcBlock][ii], r.srcBufR[r.srcBlock][ii], err
}
@@ -215,8 +216,7 @@ func (r *Resampling) at(t int64) (float64, float64, error) {
startN = 0
}
endN := int64(tInSrc + windowSize)
lv := 0.0
rv := 0.0
var lv, rv float64
var eof bool
for n := startN; n <= endN; n++ {
srcL, srcR, err := r.src(n)
@@ -259,11 +259,13 @@ func (r *Resampling) Read(b []byte) (int, error) {
n := len(b) / size * size
switch r.bitDepthInBytes {
case 2:
for i := 0; i < n/size; i++ {
for i := range n / size {
ldata, rdata, err := r.at(r.pos/int64(size) + int64(i))
if err != nil && err != io.EOF {
return 0, err
}
// EOF from the at method indicates that the source reaches the end, and doesn't indicate the resampled data ends.
// Continue the loop even if EOF is returned.
if err == io.EOF {
r.eof = true
}
@@ -273,9 +275,14 @@ func (r *Resampling) Read(b []byte) (int, error) {
b[4*i+1] = byte(l16 >> 8)
b[4*i+2] = byte(r16)
b[4*i+3] = byte(r16 >> 8)
// If the source is an io.Seeker and the length is known, check whether the resampled data ends (#3352).
if r.size > 0 && r.pos+int64(size*i) >= r.Length() {
n = size * i
break
}
}
case 4:
for i := 0; i < n/size; i++ {
for i := range n / size {
ldata, rdata, err := r.at(r.pos/int64(size) + int64(i))
if err != nil && err != io.EOF {
return 0, err
@@ -295,6 +302,10 @@ func (r *Resampling) Read(b []byte) (int, error) {
b[8*i+5] = byte(r32b >> 8)
b[8*i+6] = byte(r32b >> 16)
b[8*i+7] = byte(r32b >> 24)
if r.size > 0 && r.pos+int64(size*i) >= r.Length() {
n = size * i
break
}
}
default:
panic("not reached")

View File

@@ -164,3 +164,20 @@ func TestResampling(t *testing.T) {
})
}
}
// Issue #3352
func TestResamplingLen(t *testing.T) {
buf := make([]byte, 8*48000)
src := bytes.NewReader(buf)
resampled := convert.NewResampling(src, int64(len(buf)), 48000, 96000, 4)
if got, want := resampled.Length(), int64(len(buf)*2); got != want {
t.Errorf("got: %d, want: %d", got, want)
}
decodedBuf, err := io.ReadAll(resampled)
if err != nil {
t.Fatal(err)
}
if got, want := len(decodedBuf), int(len(buf)*2); got != want {
t.Errorf("got: %d, want: %d", got, want)
}
}