forked from go-audio/wav
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cue_chunk.go
106 lines (101 loc) · 3.95 KB
/
cue_chunk.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package wav
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/go-audio/riff"
)
// CuePoint defines an offset which marks a noteworthy sections of the audio
// content. For example, the beginning and end of a verse in a song may have cue
// points to make them easier to find.
type CuePoint struct {
// ID is the unique identifier of the cue point
ID [4]byte
// Play position specifies the sample offset associated with the cue point
// in terms of the sample's position in the final stream of samples
// generated by the play list. If a play list chunk is
// specified, the position value is equal to the sample number at which this
// cue point will occur during playback of the entire play list as defined
// by the play list's order. If no play list chunk is specified this value
// should be 0.
Position uint32
// DataChunkID - This value specifies the four byte ID used by the chunk
// containing the sample that corresponds to this cue point. A Wave file
// with no play list is always "data". A Wave file with a play list
// containing both sample data and silence may be either "data" or "slnt".
DataChunkID [4]byte
// ChunkStart specifies the byte offset into the Wave List Chunk of the
// chunk containing the sample that corresponds to this cue point. This is
// the same chunk described by the Data Chunk ID value. If no Wave List
// Chunk exists in the Wave file, this value is 0. If a Wave List Chunk
// exists, this is the offset into the "wavl" chunk. The first chunk in the
// Wave List Chunk would be specified with a value of 0.
ChunkStart uint32
// BlockStart specifies the byte offset into the "data" or "slnt" Chunk to
// the start of the block containing the sample. The start of a block is
// defined as the first byte in uncompressed PCM wave data or the last byte
// in compressed wave data where decompression can begin to find the value
// of the corresponding sample value.
BlockStart uint32
// SampleOffset specifies an offset into the block (specified by Block
// Start) for the sample that corresponds to the cue point. In uncompressed
// PCM waveform data, this is simply the byte offset into the "data" chunk.
// In compressed waveform data, this value is equal to the number of samples
// (may or may not be bytes) from the Block Start to the sample that
// corresponds to the cue point.
SampleOffset uint32
}
// DecodeCueChunk decodes the optional cue chunk and extracts cue points.
func DecodeCueChunk(d *Decoder, ch *riff.Chunk) error {
if ch == nil {
return fmt.Errorf("can't decode a nil chunk")
}
if d == nil {
return fmt.Errorf("nil decoder")
}
if ch.ID == CIDCue {
// read the entire chunk in memory
buf := make([]byte, ch.Size)
var err error
if _, err = ch.Read(buf); err != nil {
return fmt.Errorf("failed to read the CUE chunk - %v", err)
}
r := bytes.NewReader(buf)
var nbrCues uint32
if err := binary.Read(r, binary.LittleEndian, &nbrCues); err != nil {
return fmt.Errorf("failed to read the number of cues - %v", err)
}
if nbrCues > 0 {
if d.Metadata == nil {
d.Metadata = &Metadata{}
}
d.Metadata.CuePoints = []*CuePoint{}
scratch := make([]byte, 4)
for i := uint32(0); i < nbrCues; i++ {
c := &CuePoint{}
if _, err = r.Read(scratch); err != nil {
return fmt.Errorf("failed to read the cue point ID")
}
copy(c.ID[:], scratch[:4])
if err := binary.Read(r, binary.LittleEndian, &c.Position); err != nil {
return err
}
if _, err = r.Read(scratch); err != nil {
return fmt.Errorf("failed to read the data chunk id")
}
copy(c.DataChunkID[:], scratch[:4])
if err := binary.Read(r, binary.LittleEndian, &c.ChunkStart); err != nil {
return err
}
if err := binary.Read(r, binary.LittleEndian, &c.BlockStart); err != nil {
return err
}
if err := binary.Read(r, binary.LittleEndian, &c.SampleOffset); err != nil {
return err
}
d.Metadata.CuePoints = append(d.Metadata.CuePoints, c)
}
}
}
return nil
}