-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathamfm_audio.h
124 lines (101 loc) · 3.77 KB
/
amfm_audio.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
/* Copyright (c) 2018 Peter Teichman */
#ifndef AMFM_AUDIO_H
#define AMFM_AUDIO_H
#include <Audio.h>
#include "amfm.h"
#define AMFM_RINGBUF_LEN (512)
// AmFm is a combined amplitude and frequency modulation effect. The
// amplitude and frequency offsets are both provided as lookup tables,
// indexed by the phase of a rotating angle. The phases are locked
// together, so this one effect can model the doppler and volume shift
// of a single rotating speaker.
class AmFm : public AudioStream {
public:
AmFm() : AudioStream(1, inputQueueArray) {
}
void init() {
phase = 0;
setDelayDepth(0);
setTremoloDepth(0);
setRotationRate(0);
memset(ringbuf, 0, AMFM_RINGBUF_LEN * sizeof(int16_t));
}
// setDelayDepth sets the depth of the vibrato effect. This is
// provided in milliseconds. The Leslie treble horn rotates
// through 0.04064m of delay (the horn length is 8"). Assuming the
// speed of sound is 344 m/s (sea level) this means a Leslie
// induces 1.18ms of delay at its maximum.
//
// This effect incorporates a fixed length 512 sample ring
// buffer, so at 44.1kHz the maximum delay available is
// 512 * (1/44100) = 11.6ms.
void setDelayDepth(float ms) {
// Our readOffset starts from 0 (no delay) and increases from
// there, so it's always subtracted from the ring buffer write
// index.
int16_t maxDelay = (int16_t)(44.1 * ms * 256); // *256 for a <<8
// Clamp the delay to our ringbuffer length.
if (maxDelay < 0) {
maxDelay = 0;
} else if ((maxDelay >> 8) > AMFM_RINGBUF_LEN) {
maxDelay = AMFM_RINGBUF_LEN;
}
fill_sinemod(readOffset, 0, maxDelay, 0);
readOffset[256] = readOffset[0];
}
// setTremoloDepth sets the depth of the tremolo effect. This is a
// float provided in the range 0..1 (inclusive). 0 means no
// effect, 1 means the signal is attenuated all the way to 0 once
// per cycle.
void setTremoloDepth(float depth) {
int16_t maxVolume = 32767;
int16_t minVolume = (uint16_t)((float)maxVolume * (1.0 - depth));
if (minVolume < 0) {
minVolume = 0;
} else if (minVolume > maxVolume) {
minVolume = maxVolume;
}
fill_sinemod(readVolume, minVolume, maxVolume, 0);
readVolume[256] = readVolume[0];
}
// setRotationRate sets the rate of rotation of the effect (in
// cycles per second).
void setRotationRate(float hz) {
// 1<<32 / 44100 = 97391.55
phaseIncr = (uint32_t)(hz * 97391.55 + 0.5);
}
void setPhase(float norm) {
phase = (uint32_t)((float)(0xFFFFFFFF) * norm);
}
void update(void) {
audio_block_t *in = receiveReadOnly(0);
if (in == NULL) {
return;
}
audio_block_t *out = allocate();
if (out == NULL) {
release(in);
return;
}
// Making this overly complex in order to extract the logic
// into amfm.cpp for offline testing. To be cleaned up later.
amfm_update(out->data, in->data, AUDIO_BLOCK_SAMPLES, ringbuf, AMFM_RINGBUF_LEN, &wp, readVolume, readOffset, phaseIncr, &phase);
transmit(out, 0);
release(out);
release(in);
}
private:
// Ring buffer & its write position.
int16_t ringbuf[AMFM_RINGBUF_LEN];
uint32_t wp;
// Phase increment & current angle for speaker rotation.
uint32_t phaseIncr;
uint32_t phase;
// Modulation amounts for the read head & volume. This is a 256
// value array with the first item duplicated at the end, so we
// can index blindly off the end.
int16_t readOffset[257];
int16_t readVolume[257];
audio_block_t *inputQueueArray[1];
};
#endif