forked from sensorium/Mozzi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SampleHuffman.h
160 lines (129 loc) · 3.97 KB
/
SampleHuffman.h
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
154
155
156
157
158
159
160
/*
* SampleHuffman.h
*
* This file is part of Mozzi.
*
* Copyright 2013-2024 Tim Barrass and the Mozzi Team
*
* Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
*
*/
#ifndef SAMPLEHUFFMAN_H
#define SAMPLEHUFFMAN_H
#include "mozzi_pgmspace.h"
/** A sample player for samples encoded with Huffman compression.
This class and the audio2huff.py script are adapted from "audioout",
an Arduino sketch by Thomas Grill, 2011 http//grrrr.org
Huffman decoding is used on sample differentials,
saving 50-70% of space for 8 bit data, depending on the sample rate.
This implementation just plays back one sample each time next() is called, with no
speed or other adjustments.
It's slow, so it's likely you will only be able to play one sound at a time.
Audio data, Huffman decoder table, sample rate and bit depth are defined
in a sounddata.h header file. This file can be generated for a sound file with the
accompanying Python script audio2huff.py, in Mozzi/extras/python/
Invoke with:
python audio2huff.py --sndfile=arduinosnd.wav --hdrfile=sounddata.h --bits=8 --name=soundtablename
You can resample and dither your audio file with SOX,
e.g. to 8 bits depth @ Mozzi's 16384 Hz sample rate:
sox fullglory.wav -b 8 -r 16384 arduinosnd.wav
Alternatively you can export a sound from Audacity, which seems to have less noticeable or no dithering,
using Project Rate 16384 Hz and these output options:
Other uncompressed files, Header: WAV(Microsoft), Encoding: Unsigned 8 bit PCM
The header file contains two lengthy arrays:
One is "SOUNDDATA" which must fit into Flash RAM (available in total: 32k for ATMega328)
The other is "HUFFMAN" which must also fit into Flash RAM
*/
class SampleHuffman
{
public:
/** Constructor
@param SOUNDDATA the name of the SOUNDDATA table in the huffman sample .h file
@param HUFFMAN_DATA the name of the HUFFMAN table in the huffman sample .h file
@param SOUNDDATA_BITS from the huffman sample .h file
*/
SampleHuffman(uint8_t const * SOUNDDATA, int16_t const * HUFFMAN_DATA, uint32_t const SOUNDDATA_BITS):sounddata(SOUNDDATA),huffman(HUFFMAN_DATA),sounddata_bits(SOUNDDATA_BITS)
{
setLoopingOff();
}
/** Update and return the next audio sample. So far it just plays back one sample at a time without any variable tuning or speed.
@return the next audio sample
@note timing: about 5 to 40 us, varies continuously depending on data
*/
inline
int16_t next()
{
if(datapos >= sounddata_bits){
if(looping){
// at end of sample, restart from zero, looping the sound
datapos = 0;
}else{
return 0;
}
}
int16_t dif = decode();
current += dif; // add differential
return current;
}
/** Turns looping on, with the whole sample length as the loop range.
*/
inline
void setLoopingOn()
{
looping=true;
}
/** Turns looping off.
*/
inline
void setLoopingOff()
{
looping=false;
}
/** Sets the playhead to the beginning of the sample.
*/
inline
void start()
{
current = 0;
datapos = 0;
bt = 0;
}
private:
uint8_t const * sounddata;
int16_t const * huffman;
uint32_t const sounddata_bits;
uint32_t datapos; // current sample position
int16_t current; // current amplitude value
bool looping;
uint8_t bt;
// Get one bit from sound data
inline
bool getbit()
{
const uint8_t b = datapos&7;
//static uint8_t bt;
if(!b) bt = FLASH_OR_RAM_READ<const uint8_t>(sounddata+((uint32_t)datapos>>3));
// extract the indexed bit
return ((uint8_t)bt>>(7-b))&1;
}
// Decode bit stream using Huffman codes
inline
int16_t decode()
{
int16_t const * huffcode = huffman;
do {
if(getbit()) {
const int16_t offs = FLASH_OR_RAM_READ<const int16_t>(huffcode);
huffcode += offs?offs+1:2;
}
datapos++;
}
while(FLASH_OR_RAM_READ<const int16_t>(huffcode++));
return FLASH_OR_RAM_READ<const int16_t>(huffcode);
}
};
/**
@example 08.Samples/SampleHuffman_Umpah/SampleHuffman_Umpah.ino
This example demonstrates the Sample class.
*/
#endif // #ifndef SAMPLEHUFFMAN_H