This repository has been archived by the owner on Oct 8, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
DsfWriter.go
153 lines (136 loc) · 3.71 KB
/
DsfWriter.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Package dsf implements writing of audio files in the DSF (DSD Stream File) format.
package dsf
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"os"
)
const dsfBlockSize uint32 = 4096
const dsfChunkSizeDSD uint64 = 28
const dsfChunkSizeFMT uint64 = 52
const dsfChunkSizeDATA uint64 = 12
// DSFChunkDSD represents a DSD chunk.
type DSFChunkDSD struct {
Header [4]uint8
ChunkSize uint64
TotalFileSize uint64
MetaDataPtr uint64
}
// DSFChunkFMT represents a FMT chunk.
type DSFChunkFMT struct {
Header [4]uint8
ChunkSize uint64
FormatVersion uint32
FormatID uint32
ChannelType uint32
ChannelNum uint32
SamplingFreq uint32
BitsPerSample uint32
SampleCount uint64
BlockSize uint32
Reserved uint32
}
// DSFChunkDATA represents a DATA chunk header.
type DSFChunkDATA struct {
Header [4]uint8
ChunkSize uint64
}
// DSF represents a DSD Stream File (DSF).
type DSF struct {
PdmData []byte
BitRate int
}
// NewDSF creates a new DSF structure.
func NewDSF(pdmData []byte, bitRate int) *DSF {
return &DSF{PdmData: pdmData, BitRate: bitRate}
}
// ChunkFMT yields a DSF FMT chunk.
func (d *DSF) ChunkFMT() *DSFChunkFMT {
return &DSFChunkFMT{
Header: [4]byte{'f', 'm', 't', ' '},
ChunkSize: dsfChunkSizeFMT,
FormatVersion: 1,
FormatID: 0, // DSD raw
ChannelType: 1, // mono
ChannelNum: 1, // mono
SamplingFreq: uint32(d.BitRate),
BitsPerSample: 1,
SampleCount: uint64(len(d.PdmData)) * 8,
BlockSize: dsfBlockSize,
Reserved: 0,
}
}
// ChunkDSD yields a DSF DSD chunk.
func (d *DSF) ChunkDSD() *DSFChunkDSD {
totalSize := d.PaddedDataSize() + dsfChunkSizeDSD + dsfChunkSizeFMT + dsfChunkSizeDATA
return &DSFChunkDSD{
Header: [4]byte{'D', 'S', 'D', ' '},
ChunkSize: dsfChunkSizeDSD,
TotalFileSize: totalSize,
MetaDataPtr: 0,
}
}
// ChunkDATA yields a DSF DATA chunk header.
func (d *DSF) ChunkDATA() *DSFChunkDATA {
return &DSFChunkDATA{
Header: [4]byte{'d', 'a', 't', 'a'},
ChunkSize: d.PaddedDataSize() + dsfChunkSizeDATA,
}
}
// PaddedDataSize returns the padded PDM data size.
func (d *DSF) PaddedDataSize() uint64 {
return (uint64(len(d.PdmData)-1) | uint64(dsfBlockSize-1)) + 1
}
// Info reports information about the DSF object.
func (d *DSF) Info() {
duration := float64(len(d.PdmData)) * 8.0 / float64(d.BitRate)
fmt.Printf(" PDM stream: %d bits (%d bytes) @ %d bits / second\n",
len(d.PdmData)*8, len(d.PdmData), d.BitRate)
fmt.Printf(" Duration: %.2f seconds\n", duration)
fmt.Printf("Unpadded PDM data: %d bytes\n", len(d.PdmData))
fmt.Printf(" Padded PDM data: %d bytes\n", d.PaddedDataSize())
}
func closeFile(c io.Closer) {
err := c.Close()
if err != nil {
fmt.Printf("ERR: %s\n", err)
}
}
// WriteDSF writes out a DSF file.
// It returns an error upon failure.
func (d *DSF) WriteDSF(dsfFilename string) error {
f, err := os.Create(dsfFilename)
if nil != err {
return fmt.Errorf("Failed to create '%s': %v", dsfFilename, err)
}
defer closeFile(f)
err = binary.Write(f, binary.LittleEndian, d.ChunkDSD())
if nil != err {
return fmt.Errorf("Failed to write: %v", err)
}
err = binary.Write(f, binary.LittleEndian, d.ChunkFMT())
if nil != err {
return fmt.Errorf("Failed to write: %v", err)
}
err = binary.Write(f, binary.LittleEndian, d.ChunkDATA())
if nil != err {
return fmt.Errorf("Failed to write: %v", err)
}
w := bufio.NewWriter(f)
_, err = w.Write(d.PdmData)
if nil != err {
return fmt.Errorf("Failed to write: %v", err)
}
padLen := int(d.PaddedDataSize()) - len(d.PdmData)
if padLen > 0 {
padding := make([]byte, padLen)
_, err = w.Write(padding)
if nil != err {
return fmt.Errorf("Failed to write: %v", err)
}
}
_ = w.Flush()
return nil
}