forked from sensorium/Mozzi
-
Notifications
You must be signed in to change notification settings - Fork 1
/
WavePacket.h
executable file
·194 lines (159 loc) · 5.25 KB
/
WavePacket.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
* WavePacket.h
*
* Copyright 2013 Tim Barrass.
*
* This file is part of Mozzi.
*
* Mozzi is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
*
*/
#ifndef WAVEPACKET_H
#define WAVEPACKET_H
#include <MozziGuts.h>
#include <Oscil.h>
#include <tables/cos8192_int8.h>
#include <mozzi_fixmath.h>
#include <Phasor.h>
#include <Line.h>
#include <meta.h>
enum algorithms {SINGLE,DOUBLE};
/**
Wavepacket synthesis, with two overlapping streams of wave packets. Draws on
Miller Puckette's Pure Data example, F14.wave.packet.pd. Each packet is an
enveloped grain of a sin (or cos) wave. The frequency of the wave, the width of
the envelopes and the rate of release of envelopes are the parameters which can
be changed.
@tparam ALGORITHM options are SINGLE or DOUBLE, for a single non-overlapping stream of packets or a double, overlapping stream.
*/
template <int8_t ALGORITHM>
class WavePacket
{
public:
/** Constructor.
*/
WavePacket():AUDIO_STEPS_PER_CONTROL(AUDIO_RATE / CONTROL_RATE)
{
aCos.setTable(COS8192_DATA);
}
/** Set all the parameters for the synthesis.
The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
@param fundamental the rate at which packets are produced.
@param bandwidth the width of each packet. A lower value allows more
of the centre frequency to be audible, a rounder sound.
A higher value produces narrower packets, a more buzzing sound.
@param centrefreq the oscillation frequency within each packet.
*/
inline
void set(int fundamental, int bandwidth, int centrefreq)
{
setFundamental(fundamental);
setBandwidth(bandwidth);
setCentreFreq(centrefreq);
}
/** Set the fundamental frequency.
The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
@param fundamental the rate at which packets are produced.
*/
inline
void setFundamental(int fundamental)
{
aPhasor.setFreq(fundamental);
invFreq = Q8n24_FIX1 / fundamental;
}
/** Set the bandwidth.
The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
@param bandwidth the width of each packet. A lower value allows more of the
centre frequency to be audible, a rounder sound.
A higher value produces narrower packets, a more buzzing sound.
*/
inline
void setBandwidth(int bandwidth)
{
Q15n16 bw = invFreq*bandwidth;
bw >>= 9;
bw = max(bw, Q15n16_FIX1>>3);
aBandwidth.set(bw, AUDIO_STEPS_PER_CONTROL);
}
/** Set the centre frequency.
The function is designed so that usable ranges for parameters can come from analog inputs, ie. 0-1023.
@param centrefreq the oscillation frequency within each packet.
*/
inline
void setCentreFreq(int centrefreq)
{
Q15n16 cf = invFreq * centrefreq;
cf >>= 3;
aCentrefreq.set(cf, AUDIO_STEPS_PER_CONTROL);
}
/** Calculate the next synthesised sample.
@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,
then that will be yourThing.next()>>2 for HIFI 14 bit output, or >>8 for STANDARD 8+ bit output.
*/
inline
int next()
{
gcentrefreq = aCentrefreq.next();
gbandwidth = aBandwidth.next();
int phase1 = aPhasor.next()>>16; // -0.5 to 0.5, 0n15
if (ALGORITHM == DOUBLE) {
return (signalPath(params1, phase1)+signalPath(params2, phase1+32768))>>1;
} else {
return signalPath(params1, phase1);
}
}
private:
//Q15n16 fundamental; // range 4 to 6271 in pd patch, 13 integer bits
Q8n24 invFreq;
Q15n16 gcentrefreq; // range 0 to 3068, 12 integer bits
Q15n16 gbandwidth; // range 1 to 3068, 12 integer bits
// Lines to interpolate controls at audio rate
Line <Q15n16> aCentrefreq;
Line <Q16n16> aBandwidth;
Line <Q16n16> aFreq;
// different sets of params for each audio phase stream
struct parameters
{
int previous_phase;
Q15n16 centrefreq;
Q23n8 bandwidth;
}
params1,params2;
// the number of audio steps the line has to take to reach the next control value
const unsigned int AUDIO_STEPS_PER_CONTROL;
Oscil <COS8192_NUM_CELLS, AUDIO_RATE> aCos;
Phasor <AUDIO_RATE> aPhasor;
inline
int signalPath(struct parameters ¶m, int phase) // 25us? * 2
{
//setPin13High();
int index;
if(phase<param.previous_phase)
{
param.centrefreq = gcentrefreq>>8;
param.bandwidth = Q15n16_to_Q23n8(gbandwidth);
}
param.previous_phase = phase;
// oscillation
index = (param.centrefreq * phase)>>16;
// hack to make peak in middle of packet, otherwise it centres around a zero-crossing..
index += COS8192_NUM_CELLS>>1;
index &= COS8192_NUM_CELLS-1;
int8_t sig1 = aCos.atIndex(index);
// packet envelope
Q23n8 bwphase = (param.bandwidth * phase)>>8;
bwphase += COS8192_NUM_CELLS>>1;
index = constrain(bwphase, 0, (COS8192_NUM_CELLS-1));
uint8_t packet_width = 128 + aCos.atIndex(index);
// if (AUDIO_MODE == HIFI){
// return ((int) sig1 * packet_width)>>3; // scaled to fit HIFI updateAudio output
// }else{
// return ((int) sig1 * packet_width)>>8; // scaled to fit STANDARD updateAudio output
// }
return ((int) sig1 * packet_width);
}
};
/** @example 06.Synthesis/WavePacket/WavePacket.ino
This is an example of how to use the WavePacket class.
*/
#endif // #ifndef WAVEPACKET_H