fix bug leading to gaps in waveform image

This commit is contained in:
Drew Weymouth
2025-07-26 18:58:43 -07:00
parent c521c617ec
commit b9ef1e86a4
+13 -44
View File
@@ -183,11 +183,12 @@ func generateWaveformImage(ctx context.Context, data *waveformData, job *Wavefor
translucentColor := color.NRGBA{R: 255, G: 255, B: 255, A: 128} translucentColor := color.NRGBA{R: 255, G: 255, B: 255, A: 128}
for x := 0; x < 1024; x++ { for x := 0; x < 1024; x++ {
if data.progress <= x { for data.progress <= x {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
if ctx.Err() != nil { if ctx.Err() != nil {
return // expired return // expired
} }
continue
} }
rms := float64(data.RMS[x]) / 255.0 rms := float64(data.RMS[x]) / 255.0
@@ -216,6 +217,7 @@ func generateWaveformImage(ctx context.Context, data *waveformData, job *Wavefor
log.Println("done generating image") log.Println("done generating image")
} }
// assumes mono, 16 bit
func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformData, millisecs int64, fileDone func() bool) error { func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformData, millisecs int64, fileDone func() bool) error {
if fileDone() { if fileDone() {
log.Println("Analyzing completely written file!!") log.Println("Analyzing completely written file!!")
@@ -226,11 +228,9 @@ func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformDat
return err return err
} }
defer f.Close() defer f.Close()
//defer os.Remove(transcodeFile) defer os.Remove(transcodeFile)
reader := trackingReader{rs: f} decoder := wav.NewDecoder(f)
decoder := wav.NewDecoder(&reader)
if !decoder.IsValidFile() { if !decoder.IsValidFile() {
return errors.New("invalid wav file") return errors.New("invalid wav file")
} }
@@ -247,6 +247,7 @@ func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformDat
buf := &audio.IntBuffer{Data: make([]int, 4096)} buf := &audio.IntBuffer{Data: make([]int, 4096)}
curChunk := 0 curChunk := 0
chunkSamples := make([]float32, 0, samplesPerChunk) chunkSamples := make([]float32, 0, samplesPerChunk)
bytesPerSample := int64(2 * format.NumChannels) // 16-bit = 2 bytes per channel
// file read loop // file read loop
for { for {
@@ -266,10 +267,10 @@ func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformDat
} }
currentSize := stat.Size() currentSize := stat.Size()
readableBytes := currentSize - reader.Pos() // how many bytes are still available // how many bytes can we read without nearing EOF
readableBytes := currentSize - int64(samplesPerChunk)*int64(curChunk)*bytesPerSample - 16384 //buffer for safety
// Estimate how many samples we can read // Estimate how many samples we can read
bytesPerSample := int64(2 * format.NumChannels) // 16-bit = 2 bytes per channel
maxSamples := int(readableBytes / bytesPerSample) maxSamples := int(readableBytes / bytesPerSample)
if maxSamples <= 0 { if maxSamples <= 0 {
@@ -279,10 +280,7 @@ func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformDat
} }
// Resize buffer to fit only whats safe // Resize buffer to fit only whats safe
safeSamples := maxSamples safeSamples := min(maxSamples, cap(buf.Data))
if safeSamples > cap(buf.Data) {
safeSamples = cap(buf.Data)
}
buf.Data = buf.Data[:safeSamples] buf.Data = buf.Data[:safeSamples]
} else { } else {
// File is done being written, resize read buf to the max // File is done being written, resize read buf to the max
@@ -293,25 +291,20 @@ func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformDat
if n == 0 || err == io.EOF { if n == 0 || err == io.EOF {
if fileDone() { if fileDone() {
data.progress = 1024 // set progress to done data.progress = 1024 // set progress to done
break
} }
if err == io.EOF && !fileDone() { if err == io.EOF && !fileDone() {
return errors.New("WAV read got premature EOF") return errors.New("WAV read got premature EOF")
} }
break continue
} }
if err != nil { if err != nil {
return err return err
} }
// Process samples // Process samples
for i := 0; i < n; i += format.NumChannels { for i := 0; i < n; i++ {
sum := 0 sample := float32(buf.Data[i]) / float32(1<<15) // Normalize to [-1, 1]
for c := 0; c < format.NumChannels; c++ {
sum += buf.Data[i+c]
}
avg := float64(sum) / float64(format.NumChannels)
// TODO: this assumes 16 bit
sample := float32(avg / float64(1<<15)) // Normalize to [-1, 1]
chunkSamples = append(chunkSamples, sample) chunkSamples = append(chunkSamples, sample)
if len(chunkSamples) >= samplesPerChunk { if len(chunkSamples) >= samplesPerChunk {
@@ -421,27 +414,3 @@ func setPixel(img *image.NRGBA, x, y int, c color.NRGBA) {
img.Pix[offset+2] = c.B img.Pix[offset+2] = c.B
img.Pix[offset+3] = c.A img.Pix[offset+3] = c.A
} }
// wrap an io.ReadSeeker with support for tracking bytes read (Pos())
type trackingReader struct {
rs io.ReadSeeker
pos int64
}
func (t *trackingReader) Read(p []byte) (int, error) {
n, err := t.rs.Read(p)
t.pos += int64(n)
return n, err
}
func (t *trackingReader) Seek(offset int64, whence int) (int64, error) {
newPos, err := t.rs.Seek(offset, whence)
if err == nil {
t.pos = newPos
}
return newPos, err
}
func (t *trackingReader) Pos() int64 {
return t.pos
}