Mozzi  version v2.0
sound synthesis library for Arduino
SampleHuffman.h
1 /*
2  * SampleHuffman.h
3  *
4  * This file is part of Mozzi.
5  *
6  * Copyright 2013-2024 Tim Barrass and the Mozzi Team
7  *
8  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
9  *
10  */
11 
12 #ifndef SAMPLEHUFFMAN_H
13 #define SAMPLEHUFFMAN_H
14 
15 #include "mozzi_pgmspace.h"
16 
17 /** A sample player for samples encoded with Huffman compression.
18 
19 This class and the audio2huff.py script are adapted from "audioout",
20 an Arduino sketch by Thomas Grill, 2011 http//grrrr.org
21 
22 Huffman decoding is used on sample differentials,
23 saving 50-70% of space for 8 bit data, depending on the sample rate.
24 
25 This implementation just plays back one sample each time next() is called, with no
26 speed or other adjustments.
27 It's slow, so it's likely you will only be able to play one sound at a time.
28 
29 Audio data, Huffman decoder table, sample rate and bit depth are defined
30 in a sounddata.h header file. This file can be generated for a sound file with the
31 accompanying Python script audio2huff.py, in Mozzi/extras/python/
32 
33 Invoke with:
34 python audio2huff.py --sndfile=arduinosnd.wav --hdrfile=sounddata.h --bits=8 --name=soundtablename
35 
36 You can resample and dither your audio file with SOX,
37 e.g. to 8 bits depth @ Mozzi's 16384 Hz sample rate:
38 sox fullglory.wav -b 8 -r 16384 arduinosnd.wav
39 
40 Alternatively you can export a sound from Audacity, which seems to have less noticeable or no dithering,
41 using Project Rate 16384 Hz and these output options:
42 Other uncompressed files, Header: WAV(Microsoft), Encoding: Unsigned 8 bit PCM
43 
44 The header file contains two lengthy arrays:
45 One is "SOUNDDATA" which must fit into Flash RAM (available in total: 32k for ATMega328)
46 The other is "HUFFMAN" which must also fit into Flash RAM
47 
48 */
49 
51 {
52 
53 public:
54 
55  /** Constructor
56  @param SOUNDDATA the name of the SOUNDDATA table in the huffman sample .h file
57  @param HUFFMAN_DATA the name of the HUFFMAN table in the huffman sample .h file
58  @param SOUNDDATA_BITS from the huffman sample .h file
59  */
60  SampleHuffman(uint8_t const * SOUNDDATA, int16_t const * HUFFMAN_DATA, uint32_t const SOUNDDATA_BITS):sounddata(SOUNDDATA),huffman(HUFFMAN_DATA),sounddata_bits(SOUNDDATA_BITS)
61  {
63  }
64 
65 
66  /** Update and return the next audio sample. So far it just plays back one sample at a time without any variable tuning or speed.
67  @return the next audio sample
68  @note timing: about 5 to 40 us, varies continuously depending on data
69  */
70  inline
72  {
73  if(datapos >= sounddata_bits){
74  if(looping){
75  // at end of sample, restart from zero, looping the sound
76  datapos = 0;
77  }else{
78  return 0;
79  }
80  }
81 
82  int16_t dif = decode();
83  current += dif; // add differential
84  return current;
85  }
86 
87 
88  /** Turns looping on, with the whole sample length as the loop range.
89  */
90  inline
91  void setLoopingOn()
92  {
93  looping=true;
94  }
95 
96 
97  /** Turns looping off.
98  */
99  inline
101  {
102  looping=false;
103  }
104 
105  /** Sets the playhead to the beginning of the sample.
106  */
107  inline
108  void start()
109  {
110  current = 0;
111  datapos = 0;
112  bt = 0;
113  }
114 
115 private:
116  uint8_t const * sounddata;
117  int16_t const * huffman;
118  uint32_t const sounddata_bits;
119  uint32_t datapos; // current sample position
120  int16_t current; // current amplitude value
121  bool looping;
122  uint8_t bt;
123 
124  // Get one bit from sound data
125  inline
126  bool getbit()
127  {
128  const uint8_t b = datapos&7;
129  //static uint8_t bt;
130  if(!b) bt = FLASH_OR_RAM_READ<const uint8_t>(sounddata+((uint32_t)datapos>>3));
131  // extract the indexed bit
132  return ((uint8_t)bt>>(7-b))&1;
133  }
134 
135 
136  // Decode bit stream using Huffman codes
137  inline
138  int16_t decode()
139  {
140  int16_t const * huffcode = huffman;
141  do {
142  if(getbit()) {
143  const int16_t offs = FLASH_OR_RAM_READ<const int16_t>(huffcode);
144  huffcode += offs?offs+1:2;
145  }
146  datapos++;
147  }
148  while(FLASH_OR_RAM_READ<const int16_t>(huffcode++));
149  return FLASH_OR_RAM_READ<const int16_t>(huffcode);
150  }
151 
152 
153 };
154 
155 /**
156 @example 08.Samples/SampleHuffman_Umpah/SampleHuffman_Umpah.ino
157 This example demonstrates the Sample class.
158 */
159 
160 #endif // #ifndef SAMPLEHUFFMAN_H