Mercurial > hg > svapp
comparison audioio/ClipMixer.cpp @ 450:d9d132c0e240 alignment_view
Merge from default branch
author | Chris Cannam |
---|---|
date | Mon, 20 Apr 2015 09:21:32 +0100 |
parents | 88ae0e53a5da |
children |
comparison
equal
deleted
inserted
replaced
430:adfb2948fabf | 450:d9d132c0e240 |
---|---|
18 #include <sndfile.h> | 18 #include <sndfile.h> |
19 #include <cmath> | 19 #include <cmath> |
20 | 20 |
21 #include "base/Debug.h" | 21 #include "base/Debug.h" |
22 | 22 |
23 ClipMixer::ClipMixer(int channels, int sampleRate, int blockSize) : | 23 //#define DEBUG_CLIP_MIXER 1 |
24 | |
25 ClipMixer::ClipMixer(int channels, sv_samplerate_t sampleRate, sv_frame_t blockSize) : | |
24 m_channels(channels), | 26 m_channels(channels), |
25 m_sampleRate(sampleRate), | 27 m_sampleRate(sampleRate), |
26 m_blockSize(blockSize), | 28 m_blockSize(blockSize), |
27 m_clipData(0), | 29 m_clipData(0), |
28 m_clipLength(0), | 30 m_clipLength(0), |
41 { | 43 { |
42 m_channels = channels; | 44 m_channels = channels; |
43 } | 45 } |
44 | 46 |
45 bool | 47 bool |
46 ClipMixer::loadClipData(QString path, float f0, float level) | 48 ClipMixer::loadClipData(QString path, double f0, double level) |
47 { | 49 { |
48 if (m_clipData) { | 50 if (m_clipData) { |
49 cerr << "ClipMixer::loadClipData: Already have clip loaded" << endl; | 51 cerr << "ClipMixer::loadClipData: Already have clip loaded" << endl; |
50 return false; | 52 return false; |
51 } | 53 } |
52 | 54 |
53 SF_INFO info; | 55 SF_INFO info; |
54 SNDFILE *file; | 56 SNDFILE *file; |
55 float *tmpFrames; | 57 float *tmpFrames; |
56 int i; | 58 sv_frame_t i; |
57 | 59 |
58 info.format = 0; | 60 info.format = 0; |
59 file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info); | 61 file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info); |
60 if (!file) { | 62 if (!file) { |
61 cerr << "ClipMixer::loadClipData: Failed to open file path \"" | 63 cerr << "ClipMixer::loadClipData: Failed to open file path \"" |
81 | 83 |
82 for (i = 0; i < info.frames; ++i) { | 84 for (i = 0; i < info.frames; ++i) { |
83 int j; | 85 int j; |
84 m_clipData[i] = 0.0f; | 86 m_clipData[i] = 0.0f; |
85 for (j = 0; j < info.channels; ++j) { | 87 for (j = 0; j < info.channels; ++j) { |
86 m_clipData[i] += tmpFrames[i * info.channels + j] * level; | 88 m_clipData[i] += tmpFrames[i * info.channels + j] * float(level); |
87 } | 89 } |
88 } | 90 } |
89 | 91 |
90 free(tmpFrames); | 92 free(tmpFrames); |
91 | 93 |
100 ClipMixer::reset() | 102 ClipMixer::reset() |
101 { | 103 { |
102 m_playing.clear(); | 104 m_playing.clear(); |
103 } | 105 } |
104 | 106 |
105 float | 107 double |
106 ClipMixer::getResampleRatioFor(float frequency) | 108 ClipMixer::getResampleRatioFor(double frequency) |
107 { | 109 { |
108 if (!m_clipData || !m_clipRate) return 1.0; | 110 if (!m_clipData || !m_clipRate) return 1.0; |
109 float pitchRatio = m_clipF0 / frequency; | 111 double pitchRatio = m_clipF0 / frequency; |
110 float resampleRatio = m_sampleRate / m_clipRate; | 112 double resampleRatio = m_sampleRate / m_clipRate; |
111 return pitchRatio * resampleRatio; | 113 return pitchRatio * resampleRatio; |
112 } | 114 } |
113 | 115 |
114 int | 116 sv_frame_t |
115 ClipMixer::getResampledClipDuration(float frequency) | 117 ClipMixer::getResampledClipDuration(double frequency) |
116 { | 118 { |
117 return int(ceil(m_clipLength * getResampleRatioFor(frequency))); | 119 return sv_frame_t(ceil(double(m_clipLength) * getResampleRatioFor(frequency))); |
118 } | 120 } |
119 | 121 |
120 void | 122 void |
121 ClipMixer::mix(float **toBuffers, | 123 ClipMixer::mix(float **toBuffers, |
122 float gain, | 124 float gain, |
144 | 146 |
145 for (int c = 0; c < m_channels; ++c) { | 147 for (int c = 0; c < m_channels; ++c) { |
146 levels[c] = note.level * gain; | 148 levels[c] = note.level * gain; |
147 } | 149 } |
148 if (note.pan != 0.0 && m_channels == 2) { | 150 if (note.pan != 0.0 && m_channels == 2) { |
149 levels[0] *= 1.0 - note.pan; | 151 levels[0] *= 1.0f - note.pan; |
150 levels[1] *= note.pan + 1.0; | 152 levels[1] *= note.pan + 1.0f; |
151 } | 153 } |
152 | 154 |
153 int start = note.frameOffset; | 155 sv_frame_t start = note.frameOffset; |
154 int durationHere = m_blockSize; | 156 sv_frame_t durationHere = m_blockSize; |
155 if (start > 0) durationHere = m_blockSize - start; | 157 if (start > 0) durationHere = m_blockSize - start; |
156 | 158 |
157 bool ending = false; | 159 bool ending = false; |
158 | 160 |
159 foreach (NoteEnd end, endingNotes) { | 161 foreach (NoteEnd end, endingNotes) { |
165 if (start > 0) durationHere = end.frameOffset - start; | 167 if (start > 0) durationHere = end.frameOffset - start; |
166 break; | 168 break; |
167 } | 169 } |
168 } | 170 } |
169 | 171 |
170 int clipDuration = getResampledClipDuration(note.frequency); | 172 sv_frame_t clipDuration = getResampledClipDuration(note.frequency); |
171 if (start + clipDuration > 0) { | 173 if (start + clipDuration > 0) { |
172 if (start < 0 && start + clipDuration < durationHere) { | 174 if (start < 0 && start + clipDuration < durationHere) { |
173 durationHere = start + clipDuration; | 175 durationHere = start + clipDuration; |
174 } | 176 } |
175 if (durationHere > 0) { | 177 if (durationHere > 0) { |
197 | 199 |
198 void | 200 void |
199 ClipMixer::mixNote(float **toBuffers, | 201 ClipMixer::mixNote(float **toBuffers, |
200 float *levels, | 202 float *levels, |
201 float frequency, | 203 float frequency, |
202 int sourceOffset, | 204 sv_frame_t sourceOffset, |
203 int targetOffset, | 205 sv_frame_t targetOffset, |
204 int sampleCount, | 206 sv_frame_t sampleCount, |
205 bool isEnd) | 207 bool isEnd) |
206 { | 208 { |
207 if (!m_clipData) return; | 209 if (!m_clipData) return; |
208 | 210 |
209 float ratio = getResampleRatioFor(frequency); | 211 double ratio = getResampleRatioFor(frequency); |
210 | 212 |
211 float releaseTime = 0.01; | 213 double releaseTime = 0.01; |
212 int releaseSampleCount = round(releaseTime * m_sampleRate); | 214 sv_frame_t releaseSampleCount = sv_frame_t(round(releaseTime * m_sampleRate)); |
213 if (releaseSampleCount > sampleCount) { | 215 if (releaseSampleCount > sampleCount) { |
214 releaseSampleCount = sampleCount; | 216 releaseSampleCount = sampleCount; |
215 } | 217 } |
216 float releaseFraction = 1.f/releaseSampleCount; | 218 double releaseFraction = 1.0/double(releaseSampleCount); |
217 | 219 |
218 for (int i = 0; i < sampleCount; ++i) { | 220 for (sv_frame_t i = 0; i < sampleCount; ++i) { |
219 | 221 |
220 int s = sourceOffset + i; | 222 sv_frame_t s = sourceOffset + i; |
221 | 223 |
222 float os = s / ratio; | 224 double os = double(s) / ratio; |
223 int osi = int(floor(os)); | 225 sv_frame_t osi = sv_frame_t(floor(os)); |
224 | 226 |
225 //!!! just linear interpolation for now (same as SV's sample | 227 //!!! just linear interpolation for now (same as SV's sample |
226 //!!! player). a small sinc kernel would be better and | 228 //!!! player). a small sinc kernel would be better and |
227 //!!! probably "good enough" | 229 //!!! probably "good enough" |
228 float value = 0.f; | 230 double value = 0.0; |
229 if (osi < m_clipLength) { | 231 if (osi < m_clipLength) { |
230 value += m_clipData[osi]; | 232 value += m_clipData[osi]; |
231 } | 233 } |
232 if (osi + 1 < m_clipLength) { | 234 if (osi + 1 < m_clipLength) { |
233 value += (m_clipData[osi + 1] - m_clipData[osi]) * (os - osi); | 235 value += (m_clipData[osi + 1] - m_clipData[osi]) * (os - double(osi)); |
234 } | 236 } |
235 | 237 |
236 if (isEnd && i + releaseSampleCount > sampleCount) { | 238 if (isEnd && i + releaseSampleCount > sampleCount) { |
237 value *= releaseFraction * (sampleCount - i); // linear ramp for release | 239 value *= releaseFraction * double(sampleCount - i); // linear ramp for release |
238 } | 240 } |
239 | 241 |
240 for (int c = 0; c < m_channels; ++c) { | 242 for (int c = 0; c < m_channels; ++c) { |
241 toBuffers[c][targetOffset + i] += levels[c] * value; | 243 toBuffers[c][targetOffset + i] += float(levels[c] * value); |
242 } | 244 } |
243 } | 245 } |
244 } | 246 } |
245 | 247 |
246 | 248 |