Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure .vscode files should be committed, because every person usually has their own custom setup. Ofc. this is a personal repo, so feel free to ignore this.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"host": "127.0.0.1",
"program": "${workspaceRoot}/aiff",
"env": {},
"args": []
"args": ["run", "Example"]
},
{
"name": "wav tests",
Expand Down
28 changes: 16 additions & 12 deletions aiff/aiffinfo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,28 @@ func analyze(path string) {
}

d := aiff.NewDecoder(f)
frames, err := d.Frames()
pcm := d.PCM()
if err != nil {
log.Fatal(err)
}

fmt.Println("sample Rate", d.SampleRate)
fmt.Println("sample Size", d.BitDepth)
fmt.Println("number of Channels", d.NumChans)
fmt.Printf("frames: %d\n", len(frames))
fmt.Printf("frames: %d\n", pcm.Size())
fmt.Println(d)

frames, err := d.SamplesInt()
if err != nil {
log.Fatal(err)
}

max := 0
for _, f := range frames {
for _, v := range f {
if v > max {
max = v
} else if v*-1 > max {
max = v * -1
}
if f > max {
max = f
} else if f*-1 > max {
max = f * -1
}
}

Expand Down Expand Up @@ -157,7 +160,7 @@ func analyze(path string) {
// instead of graphing all points, we only take an average sample based on
// the width of the image
// TODO: smarter sampling based on duration
sampling := len(frames) / ImgWidth
sampling := (len(frames) / int(d.NumChans)) / ImgWidth
samplingCounter := make([]int, d.NumChans)
smplBuf := make([][]int, d.NumChans)
for i := 0; i < int(d.NumChans); i++ {
Expand All @@ -167,7 +170,7 @@ func analyze(path string) {
// last channel position so we can better render multi channel files
lastChanPos := make([]*point, d.NumChans)

for i := 0; i < len(frames); i++ {
for i := 0; i < len(frames); {
for channel := 0; channel < int(d.NumChans); channel++ {
if i == 0 {
lastChanPos[channel] = &point{
Expand All @@ -176,9 +179,10 @@ func analyze(path string) {
}
}
lastPos := lastChanPos[channel]
fmt.Println(channel, lastPos)
gc.MoveTo(lastPos.X, lastPos.Y)

v := frames[i][channel]
i++
v := frames[i]

// y=0 is the max, y=height-1 = is the minimun
// y=height/2 is the halfway point. We need to convert our values
Expand Down
111 changes: 22 additions & 89 deletions aiff/decoder.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package aiff

import (
"bytes"
"encoding/binary"
"errors"
"fmt"
Expand Down Expand Up @@ -97,7 +96,7 @@ func (d *Decoder) PCM() *PCM {
d.pcmClip.bitDepth = int(d.BitDepth)
d.pcmClip.sampleRate = int64(d.SampleRate)
d.pcmClip.sampleFrames = int64(d.numSampleFrames)
d.pcmClip.blockSize = size
d.pcmClip.byteSize = int(size)
// if we found the sound data before the COMM,
// we need to rewind the reader so we can properly
// set the clip reader.
Expand Down Expand Up @@ -157,96 +156,30 @@ func (d *Decoder) NextChunk() (*Chunk, error) {
return c, d.err
}

// Frames returns the audio frames contained in reader.
// SamplesInt returns the audio frames contained in the reader.
// Notes that this method allocates a lot of memory (depending on the duration of the underlying file).
// Consider using the decoder clip and reading/decoding using a buffer.
func (d *Decoder) Frames() (frames audio.Frames, err error) {
clip := d.PCM()
totalFrames := int(clip.Size())
readFrames := 0

bufSize := 4096
buf := make([]byte, bufSize)
var tFrames audio.Frames
var n int
for readFrames < totalFrames {
n, err = clip.Read(buf)
if err != nil || n == 0 {
break
}
readFrames += n
tFrames, err = d.DecodeFrames(buf)
if err != nil {
break
}
frames = append(frames, tFrames[:n]...)
}
return frames, err
func (d *Decoder) SamplesInt() (samples audio.SamplesInt, err error) {
pcm := d.PCM()
if pcm == nil {
return nil, fmt.Errorf("no PCM data available")
}
totalSamples := int(d.numSampleFrames) * int(d.NumChans)
samples = make(audio.SamplesInt, totalSamples)
n, err := pcm.Ints(samples)
return samples[:n*int(d.NumChans)], err
}

// DecodeFrames decodes PCM bytes into audio frames based on the decoder context
func (d *Decoder) DecodeFrames(data []byte) (frames audio.Frames, err error) {
numChannels := int(d.NumChans)
r := bytes.NewBuffer(data)

bytesPerSample := int((d.BitDepth-1)/8 + 1)
sampleBufData := make([]byte, bytesPerSample)

frames = make(audio.Frames, len(data)/bytesPerSample)
for j := 0; j < int(numChannels); j++ {
frames[j] = make([]int, numChannels)
}
n := 0

outter:
for i := 0; (i + (bytesPerSample * numChannels)) <= len(data); {
frame := make([]int, numChannels)
for j := 0; j < numChannels; j++ {
switch d.BitDepth {
case 8:
var v uint8
err = binary.Read(r, binary.BigEndian, &v)
if err != nil {
if err == io.EOF {
err = nil
}
break outter
}
frame[j] = int(v)
case 16:
var v int16
binary.Read(r, binary.BigEndian, &v)
frame[j] = int(v)
case 24:
_, err = r.Read(sampleBufData)
if err != nil {
if err == io.EOF {
err = nil
}
break outter
}
// TODO: check if the conversion might not be inversed depending on
// the encoding (BE vs LE)
var output int32
output |= int32(sampleBufData[2]) << 0
output |= int32(sampleBufData[1]) << 8
output |= int32(sampleBufData[0]) << 16
frame[j] = int(output)
case 32:
var v int32
binary.Read(r, binary.BigEndian, &v)
frame[j] = int(v)
default:
err = fmt.Errorf("%v bit depth not supported", d.BitDepth)
break outter
}
i += bytesPerSample
}
frames[n] = frame
n++
}

return frames, err
// SamplesFloat64 returns the audio frames contained in the reader.
// Notes that this method allocates a lot of memory (depending on the duration of the underlying file).
func (d *Decoder) SamplesFloat64() (samples audio.SamplesFloat64, err error) {
pcm := d.PCM()
if pcm == nil {
return nil, fmt.Errorf("no PCM data available")
}
totalSamples := int(d.numSampleFrames) * int(d.NumChans)
samples = make(audio.SamplesFloat64, totalSamples)
n, err := pcm.Float64s(samples)
return samples[:n*int(d.NumChans)], err
}

// Duration returns the time duration for the current AIFF container
Expand Down
6 changes: 3 additions & 3 deletions aiff/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ func Test_Frames(t *testing.T) {
}
d := NewDecoder(in)
clip := d.PCM()
frames, err := d.Frames()
frames, err := d.SamplesInt()
if err != nil {
t.Fatal(err)
}
if int(clip.Size()) != len(frames) {
t.Fatalf("expected %d frames, got %d", clip.Size(), len(frames))
if int(clip.Size()) != len(frames)/int(d.NumChans) {
t.Fatalf("expected %d frames, got %d", clip.Size(), len(frames)/int(d.NumChans))
}
}
}
Expand Down
Loading