Mozzi  version v2.0
sound synthesis library for Arduino
WavePacket.h
1 /*
2  * WavePacket.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 
13 #ifndef WAVEPACKET_H
14 #define WAVEPACKET_H
15 
16 #include "MozziHeadersOnly.h"
17 #include "Oscil.h"
18 #include "tables/cos8192_int8.h"
19 #include "mozzi_fixmath.h"
20 #include "Phasor.h"
21 #include "Line.h"
22 #include "meta.h"
23 
24 
25 enum algorithms {SINGLE,DOUBLE};
26 
27 /**
28 Wavepacket synthesis, with two overlapping streams of wave packets. Draws on
29 Miller Puckette's Pure Data example, F14.wave.packet.pd. Each packet is an
30 enveloped grain of a sin (or cos) wave. The frequency of the wave, the width of
31 the envelopes and the rate of release of envelopes are the parameters which can
32 be changed.
33 @tparam ALGORITHM options are SINGLE or DOUBLE, for a single non-overlapping stream of packets or a double, overlapping stream.
34 */
35 template <int8_t ALGORITHM>
37 {
38 public:
39 
40  /** Constructor.
41  */
43  {
44  aCos.setTable(COS8192_DATA);
45  }
46 
47 
48  /** Set all the parameters for the synthesis.
49  The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
50  @param fundamental the rate at which packets are produced.
51  @param bandwidth the width of each packet. A lower value allows more
52  of the centre frequency to be audible, a rounder sound.
53  A higher value produces narrower packets, a more buzzing sound.
54  @param centrefreq the oscillation frequency within each packet.
55  */
56  inline
57  void set(int fundamental, int bandwidth, int centrefreq)
58  {
59  setFundamental(fundamental);
60  setBandwidth(bandwidth);
61  setCentreFreq(centrefreq);
62  }
63 
64 
65  /** Set the fundamental frequency.
66  The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
67  @param fundamental the rate at which packets are produced.
68  */
69  inline
70  void setFundamental(int fundamental)
71  {
72  aPhasor.setFreq(fundamental);
73  invFreq = Q8n24_FIX1 / fundamental;
74  }
75 
76 
77 
78  /** Set the bandwidth.
79  The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
80  @param bandwidth the width of each packet. A lower value allows more of the
81  centre frequency to be audible, a rounder sound.
82  A higher value produces narrower packets, a more buzzing sound.
83  */
84  inline
85  void setBandwidth(int bandwidth)
86  {
87  Q15n16 bw = invFreq*bandwidth;
88  bw >>= 9;
89  bw = max(bw, Q15n16_FIX1>>3);
90  aBandwidth.set(bw, AUDIO_STEPS_PER_CONTROL);
91  }
92 
93 
94 
95  /** Set the centre frequency.
96  The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
97  @param centrefreq the oscillation frequency within each packet.
98  */
99  inline
100  void setCentreFreq(int centrefreq)
101  {
102  Q15n16 cf = invFreq * centrefreq;
103  cf >>= 3;
104  aCentrefreq.set(cf, AUDIO_STEPS_PER_CONTROL);
105  }
106 
107 
108 /** Calculate the next synthesised sample.
109 @return a full-scale 16 bit value, which needs to be scaled to suit your sketch. If you're using it straight as the sketch output,
110 then that will be yourThing.next()>>2 for HIFI 14 bit output, or >>8 for STANDARD 8+ bit output.
111 */
112  inline
113  int next()
114  {
115  gcentrefreq = aCentrefreq.next();
116  gbandwidth = aBandwidth.next();
117  int phase1 = aPhasor.next()>>16; // -0.5 to 0.5, 0n15
118  if (ALGORITHM == DOUBLE) {
119  return (signalPath(params1, phase1)+signalPath(params2, phase1+32768))>>1;
120  } else {
121  return signalPath(params1, phase1);
122  }
123  }
124 
125 
126 
127 private:
128  //Q15n16 fundamental; // range 4 to 6271 in pd patch, 13 integer bits
129  Q8n24 invFreq;
130  Q15n16 gcentrefreq; // range 0 to 3068, 12 integer bits
131  Q15n16 gbandwidth; // range 1 to 3068, 12 integer bits
132 
133  // Lines to interpolate controls at audio rate
134  Line <Q15n16> aCentrefreq;
135  Line <Q16n16> aBandwidth;
136  Line <Q16n16> aFreq;
137 
138  // different sets of params for each audio phase stream
139  struct parameters
140  {
141  int previous_phase;
142  Q15n16 centrefreq;
143  Q23n8 bandwidth;
144  }
145  params1,params2;
146 
147  // the number of audio steps the line has to take to reach the next control value
148  const unsigned int AUDIO_STEPS_PER_CONTROL;
149 
150  Oscil <COS8192_NUM_CELLS, MOZZI_AUDIO_RATE> aCos;
151  Phasor <MOZZI_AUDIO_RATE> aPhasor;
152 
153 
154  inline
155  int signalPath(struct parameters &param, int phase) // 25us? * 2
156  {
157  //setPin13High();
158  int index;
159 
160  if(phase<param.previous_phase)
161  {
162  param.centrefreq = gcentrefreq>>8;
163  param.bandwidth = Q15n16_to_Q23n8(gbandwidth);
164  }
165  param.previous_phase = phase;
166 
167  // oscillation
168  index = (param.centrefreq * phase)>>16;
169  // hack to make peak in middle of packet, otherwise it centres around a zero-crossing..
170  index += COS8192_NUM_CELLS>>1;
171  index &= COS8192_NUM_CELLS-1;
172  int8_t sig1 = aCos.atIndex(index);
173 
174  // packet envelope
175  Q23n8 bwphase = (param.bandwidth * phase)>>8;
176  bwphase += COS8192_NUM_CELLS>>1;
177  index = constrain(bwphase, 0, (COS8192_NUM_CELLS-1));
178  uint8_t packet_width = 128 + aCos.atIndex(index);
179  // if (AUDIO_MODE == HIFI){
180  // return ((int) sig1 * packet_width)>>3; // scaled to fit HIFI updateAudio output
181  // }else{
182  // return ((int) sig1 * packet_width)>>8; // scaled to fit STANDARD updateAudio output
183  // }
184 
185  return ((int) sig1 * packet_width);
186  }
187 
188 };
189 
190 /** @example 06.Synthesis/WavePacket/WavePacket.ino
191 This is an example of how to use the WavePacket class.
192 */
193 
194 #endif // #ifndef WAVEPACKET_H