Skip to content

Commit 3bf6c7c

Browse files
committed
FEAT: basic WAV codec (decoder) written directly in Rebol
1 parent d98b9ee commit 3bf6c7c

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed

src/mezz/codec-wav.r

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
REBOL [
2+
title: "REBOL 3 codec for WAV file format"
3+
name: 'codec-WAV
4+
author: "Oldes"
5+
version: 0.1.0
6+
date: 11-Oct-2018
7+
history: [
8+
0.1.0 11-Oct-2018 "Oldes" {
9+
Initial version with DECODE and IDENTIFY functions.
10+
Not all chunks are parsed.
11+
}
12+
]
13+
]
14+
15+
register-codec [
16+
name: 'WAV
17+
title: "Waveform Audio File Format"
18+
suffixes: [%.wav %.wave]
19+
20+
decode: function [
21+
data [binary!]
22+
][
23+
if verbose > 0 [
24+
print ["^[[1;32mDecode WAV data^[[m (^[[1m" length? data "^[[mbytes )"]
25+
; count maximal bytes width (used for padding)
26+
w: 1 + length? form length? data
27+
]
28+
29+
bin: binary data
30+
binary/read bin [
31+
chunkId: BYTES 4
32+
chunkSize: UI32LE
33+
format: BYTES 4
34+
]
35+
36+
if all [
37+
chunkId = #{52494646} ;RIFF
38+
format = #{57415645} ;WAVE
39+
][
40+
; looks like valid WAVE file
41+
data: copy []
42+
chunks: copy []
43+
44+
while [not tail? bin/buffer][
45+
binary/read bin [
46+
id: BYTES 4
47+
size: UI32LE
48+
starts: INDEX
49+
]
50+
ends: starts + size
51+
chunk: any [ try [to tag! id] id ]
52+
if verbose > 0 [
53+
printf [
54+
$32
55+
"CHUNK: " $1 7 $0
56+
"at: " $1 w $0
57+
"bytes: " $1 w $0
58+
] reduce [mold chunk starts size]
59+
]
60+
append chunks chunk
61+
switch/default chunk [
62+
<fmt > [
63+
format: binary/read bin [
64+
UI16LE ; compression
65+
UI16LE ; channels
66+
UI32LE ; sampleRate
67+
UI32LE ; bytesPerSec
68+
UI16LE ; blockAlign
69+
UI16LE ; bitsPerSample
70+
]
71+
if size > 16 [
72+
size: size - 16
73+
append format copy/part bin/buffer size
74+
bin/buffer: skip bin/buffer size
75+
]
76+
append/only chunks format
77+
]
78+
<data> [
79+
binary/read/into bin [BYTES :size] tail data
80+
append chunks size ; so one could reconstruct the data chunk in encoding
81+
]
82+
<smpl> [
83+
sampler: binary/read bin [
84+
UI32LE ; Manufacturer
85+
UI32LE ; Product
86+
UI32LE ; Sample Period
87+
UI32LE ; MIDI Unity Note
88+
UI32LE ; MIDI Pitch Fraction
89+
UI32LE ; SMPTE Format
90+
UI32LE ; SMPTE Offset
91+
count: UI32LE ; Num Sample Loops
92+
UI32LE ; Sampler Data
93+
]
94+
append/only sampler loops: copy []
95+
loop count [
96+
binary/read/into bin [
97+
UI32LE ; Cue Point ID
98+
UI32LE ; Type
99+
UI32LE ; Start
100+
UI32LE ; End
101+
UI32LE ; Fraction
102+
UI32LE ; Play Count
103+
] loops
104+
]
105+
append/only chunks sampler
106+
]
107+
<fact> [
108+
binary/read/into bin [BYTES :size] tail chunks
109+
]
110+
<cue > [
111+
count: binary/read bin 'UI32LE
112+
append chunks cues: copy []
113+
loop count [
114+
binary/read/into bin [
115+
UI32LE ; id - unique identification value
116+
UI32LE ; Position - play order position
117+
UI32LE ; Data Chunk ID - RIFF ID of corresponding data chunk
118+
UI32LE ; Chunk Start - Byte Offset of Data Chunk *
119+
UI32LE ; Block Start - Byte Offset to sample of First Channel
120+
UI32LE ; Sample Offset - Byte Offset to sample byte of First Channel
121+
] tail cues
122+
]
123+
new-line/skip cues true 6
124+
]
125+
<_PMX> [
126+
; Extensible Metadata Platform (XMP) data
127+
;@@ https://www.adobe.com/products/xmp.html
128+
binary/read bin [tmp: BYTES :size]
129+
try [tmp: to string! tmp]
130+
append chunks tmp
131+
if verbose > 1 [printf [$33 tmp $0] ""]
132+
]
133+
][
134+
binary/read/into bin [BYTES :size] tail chunks
135+
]
136+
if ends <> index? bin/buffer [
137+
cause-error 'script 'bad-bad ["WAV decode" "invalid chunk end"]
138+
]
139+
]
140+
]
141+
either any [empty? chunks none? format] [
142+
none
143+
][
144+
new-line/skip chunks true 2
145+
object compose/only [
146+
type: 'wave
147+
rate: (format/3)
148+
channels: (format/2)
149+
bits: (format/6)
150+
chunks: (chunks)
151+
data: (either empty? data [none][rejoin data])
152+
]
153+
]
154+
]
155+
identify: func [
156+
"Returns TRUE if binary looks like WAV data"
157+
data [binary!]
158+
][
159+
parse data [#{52494646} 4 skip #{57415645} to end]
160+
]
161+
162+
verbose: 0
163+
]

src/tests/run-tests.r3

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ wrap load %units/conditional-test.r3
2323
wrap load %units/protect-test.r3
2424
wrap load %units/crash-test.r3
2525
wrap load %units/bincode-test.r3
26+
wrap load %units/codecs-test.r3
2627

2728
recycle/torture
2829
recycle

src/tests/units/codecs-test.r3

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Rebol [
2+
Title: "Rebol codecs test script"
3+
Author: "Oldes"
4+
File: %codecs-test.r3
5+
Tabs: 4
6+
Needs: [%../quick-test-module.r3]
7+
]
8+
9+
~~~start-file~~~ "Codecs"
10+
11+
if find system/codecs 'wav [
12+
system/codecs/wav/verbose: 3
13+
===start-group=== "WAV codec"
14+
15+
--test-- "Load WAV file"
16+
--assert object? snd: load %units/files/drumloop.wav
17+
--assert 'wave = snd/type
18+
--assert 44100 = snd/rate
19+
--assert 1 = snd/channels
20+
--assert 16 = snd/bits
21+
--assert 3097828 = checksum snd/data
22+
snd: none
23+
--test-- "Decode WAV data"
24+
--assert binary? bin: read %units/files/zblunk_02.wav
25+
--assert object? snd: decode 'WAV bin
26+
--assert 4283614 = checksum snd/data
27+
snd: none
28+
bin: none
29+
30+
===end-group===
31+
system/codecs/wav/verbose: 0
32+
]
33+
34+
~~~end-file~~~

src/tests/units/files/drumloop.wav

82.4 KB
Binary file not shown.

src/tests/units/files/zblunk_02.wav

58.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)