comparison audioio/ClipMixer.cpp @ 310:8c59cc68eabd tonioni

Initial implementation of clip note mix
author Chris Cannam
date Tue, 07 Jan 2014 17:11:14 +0000
parents 289d65722123
children 86fb9160d122
comparison
equal deleted inserted replaced
309:71050ffd0141 310:8c59cc68eabd
14 */ 14 */
15 15
16 #include "ClipMixer.h" 16 #include "ClipMixer.h"
17 17
18 #include <sndfile.h> 18 #include <sndfile.h>
19 #include <cmath>
19 20
20 #include "base/Debug.h" 21 #include "base/Debug.h"
21 22
22 ClipMixer::ClipMixer(int channels, int sampleRate, int blockSize) : 23 ClipMixer::ClipMixer(int channels, int sampleRate, int blockSize) :
23 m_channels(channels), 24 m_channels(channels),
87 free(tmpFrames); 88 free(tmpFrames);
88 89
89 m_clipLength = info.frames; 90 m_clipLength = info.frames;
90 m_clipF0 = f0; 91 m_clipF0 = f0;
91 m_clipRate = info.samplerate; 92 m_clipRate = info.samplerate;
93
94 return true;
92 } 95 }
93 96
94 void 97 void
95 ClipMixer::reset() 98 ClipMixer::reset()
96 { 99 {
97 //!!! 100 m_playing.clear();
101 }
102
103 float
104 ClipMixer::getResampleRatioFor(float frequency)
105 {
106 if (!m_clipData) return 1.0;
107 float pitchRatio = frequency / m_clipF0;
108 float resampleRatio = m_sampleRate / m_clipRate;
109 return pitchRatio * resampleRatio;
110 }
111
112 int
113 ClipMixer::getResampledClipDuration(float frequency)
114 {
115 return int(ceil(m_clipLength * getResampleRatioFor(frequency)));
98 } 116 }
99 117
100 void 118 void
101 ClipMixer::mix(float **toBuffers, 119 ClipMixer::mix(float **toBuffers,
102 float gain, 120 float gain,
103 std::vector<NoteStart> newNotes, 121 std::vector<NoteStart> newNotes,
104 std::vector<NoteEnd> endingNotes) 122 std::vector<NoteEnd> endingNotes)
105 { 123 {
106 //!!! do this! 124 foreach (NoteStart note, newNotes) {
107 } 125 m_playing.push_back(note);
108 126 }
127
128 std::vector<NoteStart> remaining;
129
130 float *levels = new float[m_channels];
131
132 foreach (NoteStart note, m_playing) {
133
134 for (int c = 0; c < m_channels; ++c) {
135 levels[c] = gain;
136 }
137 if (note.pan != 0.0 && m_channels == 2) {
138 levels[0] *= 1.0 - note.pan;
139 levels[1] *= note.pan + 1.0;
140 }
141
142 int start = note.frameOffset;
143 int durationHere = m_blockSize;
144 if (start > 0) durationHere = m_blockSize - start;
145
146 bool ending = false;
147
148 foreach (NoteEnd end, endingNotes) {
149 if (end.frequency == note.frequency &&
150 end.frameOffset >= start &&
151 end.frameOffset <= m_blockSize) {
152 ending = true;
153 durationHere = end.frameOffset;
154 if (start > 0) durationHere = end.frameOffset - start;
155 break;
156 }
157 }
158
159 int clipDuration = getResampledClipDuration(note.frequency);
160 if (start + clipDuration > 0) {
161 if (start < 0 && start + clipDuration < durationHere) {
162 durationHere = clipDuration - start;
163 }
164 if (durationHere > 0) {
165 mixNote(toBuffers,
166 levels,
167 note.frequency,
168 start < 0 ? -start : 0,
169 start > 0 ? start : 0,
170 durationHere);
171 }
172 }
173
174 if (!ending) {
175 NoteStart adjusted = note;
176 adjusted.frameOffset -= m_blockSize;
177 remaining.push_back(adjusted);
178 }
179 }
180
181 m_playing = remaining;
182 }
183
184 void
185 ClipMixer::mixNote(float **toBuffers,
186 float *levels,
187 float frequency,
188 int sourceOffset,
189 int targetOffset,
190 int sampleCount)
191 {
192 if (!m_clipData) return;
193
194 float ratio = getResampleRatioFor(frequency);
195
196 //!!! todo: release time
197
198 for (int i = 0; i < sampleCount; ++i) {
199
200 int s = sourceOffset + i;
201
202 float os = s / ratio;
203 int osi = int(floor(os));
204
205 //!!! just linear interpolation for now (same as SV's sample
206 //!!! player). a small sinc kernel would be better and
207 //!!! probably "good enough"
208 float value = 0.f;
209 if (osi < m_clipLength) {
210 value += m_clipData[osi];
211 }
212 if (osi + 1 < m_clipLength) {
213 value += (m_clipData[osi + 1] - m_clipData[osi]) * (os - osi);
214 }
215
216 for (int c = 0; c < m_channels; ++c) {
217 toBuffers[c][targetOffset + i] = levels[c] * value;
218 }
219 }
220 }
221
222