Skip to content

Commit

Permalink
Revert "feat: enhance AudioBuffer with sample tracking and segment ex…
Browse files Browse the repository at this point in the history
…traction"

This reverts commit a433fa6.
  • Loading branch information
tphakala committed Jan 4, 2025
1 parent 79ad8ce commit 175dd9c
Showing 1 changed file with 13 additions and 92 deletions.
105 changes: 13 additions & 92 deletions internal/myaudio/audiobuffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,9 @@ import (
"github.com/tphakala/birdnet-go/internal/conf"
)

// SegmentInfo contains audio segment data and sample tracking information
type SegmentInfo struct {
Data []byte
StartSampleIdx uint64
EndSampleIdx uint64
HasDiscontinuity bool
DiscontinuityAt []int
}

// AudioBuffer represents a circular buffer for storing PCM audio data, with timestamp tracking.
type AudioBuffer struct {
data []byte
sampleIndices []uint64
lastSampleIdx uint64
writeIndex int
sampleRate int
bytesPerSample int
Expand All @@ -48,20 +37,16 @@ func InitAudioBuffers(durationSeconds int, sampleRate, bytesPerSample int, sourc
// NewAudioBuffer initializes a new AudioBuffer with timestamp tracking
func NewAudioBuffer(durationSeconds int, sampleRate, bytesPerSample int) *AudioBuffer {
bufferSize := durationSeconds * sampleRate * bytesPerSample
alignedBufferSize := ((bufferSize + 2047) / 2048) * 2048
numSamples := alignedBufferSize / bytesPerSample

alignedBufferSize := ((bufferSize + 2047) / 2048) * 2048 // Round up to the nearest multiple of 2048
ab := &AudioBuffer{
data: make([]byte, alignedBufferSize),
sampleIndices: make([]uint64, numSamples),
lastSampleIdx: 0,
sampleRate: sampleRate,
bytesPerSample: bytesPerSample,
bufferSize: alignedBufferSize,

bufferDuration: time.Second * time.Duration(durationSeconds),
initialized: false,
}

return ab
}

Expand All @@ -77,7 +62,7 @@ func WriteToCaptureBuffer(source string, data []byte) {
}

// ReadSegment extracts a segment of audio data from the buffer for a given source.
func ReadSegmentFromCaptureBuffer(source string, requestedStartTime time.Time, duration int) (*SegmentInfo, error) {
func ReadSegmentFromCaptureBuffer(source string, requestedStartTime time.Time, duration int) ([]byte, error) {
ab, exists := audioBuffers[source]
if !exists {
return nil, fmt.Errorf("No audio buffer found for source: %s", source)
Expand All @@ -100,32 +85,14 @@ func (ab *AudioBuffer) Write(data []byte) {

// Store the current write index to determine if we've wrapped around the buffer.
prevWriteIndex := ab.writeIndex
samplesWritten := 0

// Write data and sample indices
for i := 0; i < len(data); i += ab.bytesPerSample {
dataIdx := ab.writeIndex + i
if dataIdx >= ab.bufferSize {
dataIdx -= ab.bufferSize
}

// Copy audio data
for j := 0; j < ab.bytesPerSample && i+j < len(data); j++ {
ab.data[dataIdx+j] = data[i+j]
}

// Update sample index
sampleIdx := (ab.writeIndex + i) / ab.bytesPerSample
if sampleIdx >= len(ab.sampleIndices) {
sampleIdx -= len(ab.sampleIndices)
}
ab.sampleIndices[sampleIdx] = ab.lastSampleIdx + uint64(samplesWritten)
samplesWritten++
}
// Copy the incoming data into the buffer starting at the current write index.
bytesWritten := copy(ab.data[ab.writeIndex:], data)

ab.writeIndex = (ab.writeIndex + len(data)) % ab.bufferSize
ab.lastSampleIdx += uint64(samplesWritten)
// Update the write index, wrapping around the buffer if necessary.
ab.writeIndex = (ab.writeIndex + bytesWritten) % ab.bufferSize

// Determine if the write operation has overwritten old data.
if ab.writeIndex <= prevWriteIndex {
// If old data has been overwritten, adjust startTime to maintain accurate timekeeping.
ab.startTime = time.Now().Add(-ab.bufferDuration)
Expand All @@ -137,7 +104,7 @@ func (ab *AudioBuffer) Write(data []byte) {

// ReadSegment extracts a segment of audio data based on precise start and end times, handling wraparounds.
// It waits until the current time is past the requested end time.
func (ab *AudioBuffer) ReadSegment(requestedStartTime time.Time, duration int) (*SegmentInfo, error) {
func (ab *AudioBuffer) ReadSegment(requestedStartTime time.Time, duration int) ([]byte, error) {
requestedEndTime := requestedStartTime.Add(time.Duration(duration) * time.Second)

for {
Expand Down Expand Up @@ -168,27 +135,13 @@ func (ab *AudioBuffer) ReadSegment(requestedStartTime time.Time, duration int) (
// Wait until the current time is past the requested end time
if time.Now().After(requestedEndTime) {
var segment []byte
var startSampleIdx, endSampleIdx uint64
discontinuities := make([]int, 0)

if startIndex < endIndex {
if conf.Setting().Realtime.Audio.Export.Debug {
log.Printf("Reading segment from %d to %d", startIndex, endIndex)
}
segmentSize := endIndex - startIndex
segment = make([]byte, segmentSize)
copy(segment, ab.data[startIndex:endIndex])

// Track sample indices
startSampleIdx = ab.sampleIndices[startIndex/ab.bytesPerSample]
endSampleIdx = ab.sampleIndices[(endIndex/ab.bytesPerSample)-1] + 1

// Check for discontinuities
for i := startIndex/ab.bytesPerSample + 1; i < endIndex/ab.bytesPerSample; i++ {
if ab.sampleIndices[i] != ab.sampleIndices[i-1]+1 {
discontinuities = append(discontinuities, (i*ab.bytesPerSample)-startIndex)
}
}
} else {
if conf.Setting().Realtime.Audio.Export.Debug {
log.Printf("Buffer wrapped during read, reading segment from %d to %d", startIndex, endIndex)
Expand All @@ -198,46 +151,14 @@ func (ab *AudioBuffer) ReadSegment(requestedStartTime time.Time, duration int) (
firstPartSize := ab.bufferSize - startIndex
copy(segment[:firstPartSize], ab.data[startIndex:])
copy(segment[firstPartSize:], ab.data[:endIndex])

// Track sample indices for wrapped buffer
startSampleIdx = ab.sampleIndices[startIndex/ab.bytesPerSample]
endSampleIdx = ab.sampleIndices[(endIndex/ab.bytesPerSample)-1] + 1

// Check for discontinuities including wrap point
for i := startIndex / ab.bytesPerSample; i < ab.bufferSize/ab.bytesPerSample; i++ {
if i > startIndex/ab.bytesPerSample && i > 0 {
if ab.sampleIndices[i] != ab.sampleIndices[i-1]+1 {
discontinuities = append(discontinuities, (i*ab.bytesPerSample)-startIndex)
}
}
}
for i := 1; i < endIndex/ab.bytesPerSample; i++ {
if ab.sampleIndices[i] != ab.sampleIndices[i-1]+1 {
discontinuities = append(discontinuities, ((i*ab.bytesPerSample)+firstPartSize)-startIndex)
}
}
}

if conf.Setting().Realtime.Audio.Export.Debug {
if len(discontinuities) > 0 {
log.Printf("Found %d discontinuities in samples %d to %d at positions: %v",
len(discontinuities), startSampleIdx, endSampleIdx, discontinuities)
} else {
log.Printf("Sample sequence continuous from %d to %d",
startSampleIdx, endSampleIdx)
}
}

ab.lock.Unlock()
return &SegmentInfo{
Data: segment,
StartSampleIdx: startSampleIdx,
EndSampleIdx: endSampleIdx,
HasDiscontinuity: len(discontinuities) > 0,
DiscontinuityAt: discontinuities,
}, nil
return segment, nil
}

if conf.Setting().Realtime.Audio.Export.Debug {
log.Printf("Buffer is not filled yet, waiting for data to be available")
}
ab.lock.Unlock()
time.Sleep(1 * time.Second) // Sleep briefly to avoid busy waiting
}
Expand Down

0 comments on commit 175dd9c

Please sign in to comment.