annotate audioio/ClipMixer.cpp @ 425:5882462fa747 alignment_view

Seems more logical for the external alignment program to emit reference,other rather than other,reference
author Chris Cannam
date Thu, 20 Nov 2014 17:17:45 +0000
parents b0b49da13975
children 72c662fe7ea3
rev   line source
Chris@305 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@305 2
Chris@305 3 /*
Chris@305 4 Sonic Visualiser
Chris@305 5 An audio file viewer and annotation editor.
Chris@305 6 Centre for Digital Music, Queen Mary, University of London.
Chris@305 7 This file copyright 2006 Chris Cannam, 2006-2014 QMUL.
Chris@305 8
Chris@305 9 This program is free software; you can redistribute it and/or
Chris@305 10 modify it under the terms of the GNU General Public License as
Chris@305 11 published by the Free Software Foundation; either version 2 of the
Chris@305 12 License, or (at your option) any later version. See the file
Chris@305 13 COPYING included with this distribution for more information.
Chris@305 14 */
Chris@305 15
Chris@305 16 #include "ClipMixer.h"
Chris@305 17
Chris@305 18 #include <sndfile.h>
Chris@310 19 #include <cmath>
Chris@305 20
Chris@305 21 #include "base/Debug.h"
Chris@305 22
Chris@305 23 ClipMixer::ClipMixer(int channels, int sampleRate, int blockSize) :
Chris@305 24 m_channels(channels),
Chris@305 25 m_sampleRate(sampleRate),
Chris@305 26 m_blockSize(blockSize),
Chris@407 27 m_clipData(0),
Chris@407 28 m_clipLength(0),
Chris@407 29 m_clipF0(0),
Chris@407 30 m_clipRate(0)
Chris@305 31 {
Chris@305 32 }
Chris@305 33
Chris@305 34 ClipMixer::~ClipMixer()
Chris@305 35 {
Chris@317 36 if (m_clipData) free(m_clipData);
Chris@305 37 }
Chris@305 38
Chris@308 39 void
Chris@308 40 ClipMixer::setChannelCount(int channels)
Chris@308 41 {
Chris@308 42 m_channels = channels;
Chris@308 43 }
Chris@308 44
Chris@305 45 bool
Chris@349 46 ClipMixer::loadClipData(QString path, float f0, float level)
Chris@305 47 {
Chris@305 48 if (m_clipData) {
Chris@307 49 cerr << "ClipMixer::loadClipData: Already have clip loaded" << endl;
Chris@305 50 return false;
Chris@305 51 }
Chris@305 52
Chris@305 53 SF_INFO info;
Chris@305 54 SNDFILE *file;
Chris@305 55 float *tmpFrames;
Chris@366 56 int i;
Chris@305 57
Chris@305 58 info.format = 0;
Chris@305 59 file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
Chris@305 60 if (!file) {
Chris@308 61 cerr << "ClipMixer::loadClipData: Failed to open file path \""
Chris@308 62 << path << "\": " << sf_strerror(file) << endl;
Chris@305 63 return false;
Chris@305 64 }
Chris@305 65
Chris@305 66 tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float));
Chris@305 67 if (!tmpFrames) {
Chris@307 68 cerr << "ClipMixer::loadClipData: malloc(" << info.frames * info.channels * sizeof(float) << ") failed" << endl;
Chris@305 69 return false;
Chris@305 70 }
Chris@305 71
Chris@305 72 sf_readf_float(file, tmpFrames, info.frames);
Chris@305 73 sf_close(file);
Chris@305 74
Chris@305 75 m_clipData = (float *)malloc(info.frames * sizeof(float));
Chris@305 76 if (!m_clipData) {
Chris@307 77 cerr << "ClipMixer::loadClipData: malloc(" << info.frames * sizeof(float) << ") failed" << endl;
Chris@305 78 free(tmpFrames);
Chris@305 79 return false;
Chris@305 80 }
Chris@305 81
Chris@305 82 for (i = 0; i < info.frames; ++i) {
Chris@305 83 int j;
Chris@305 84 m_clipData[i] = 0.0f;
Chris@305 85 for (j = 0; j < info.channels; ++j) {
Chris@349 86 m_clipData[i] += tmpFrames[i * info.channels + j] * level;
Chris@305 87 }
Chris@305 88 }
Chris@305 89
Chris@305 90 free(tmpFrames);
Chris@305 91
Chris@305 92 m_clipLength = info.frames;
Chris@305 93 m_clipF0 = f0;
Chris@305 94 m_clipRate = info.samplerate;
Chris@310 95
Chris@310 96 return true;
Chris@305 97 }
Chris@307 98
Chris@307 99 void
Chris@308 100 ClipMixer::reset()
Chris@308 101 {
Chris@310 102 m_playing.clear();
Chris@310 103 }
Chris@310 104
Chris@310 105 float
Chris@310 106 ClipMixer::getResampleRatioFor(float frequency)
Chris@310 107 {
Chris@407 108 if (!m_clipData || !m_clipRate) return 1.0;
Chris@311 109 float pitchRatio = m_clipF0 / frequency;
Chris@310 110 float resampleRatio = m_sampleRate / m_clipRate;
Chris@310 111 return pitchRatio * resampleRatio;
Chris@310 112 }
Chris@310 113
Chris@310 114 int
Chris@310 115 ClipMixer::getResampledClipDuration(float frequency)
Chris@310 116 {
Chris@310 117 return int(ceil(m_clipLength * getResampleRatioFor(frequency)));
Chris@308 118 }
Chris@308 119
Chris@308 120 void
Chris@307 121 ClipMixer::mix(float **toBuffers,
Chris@308 122 float gain,
Chris@307 123 std::vector<NoteStart> newNotes,
Chris@307 124 std::vector<NoteEnd> endingNotes)
Chris@307 125 {
Chris@310 126 foreach (NoteStart note, newNotes) {
matthiasm@321 127 if (note.frequency > 20 &&
matthiasm@322 128 note.frequency < 5000) {
matthiasm@321 129 m_playing.push_back(note);
matthiasm@321 130 }
Chris@310 131 }
Chris@310 132
Chris@310 133 std::vector<NoteStart> remaining;
Chris@310 134
Chris@310 135 float *levels = new float[m_channels];
Chris@310 136
Chris@397 137 #ifdef DEBUG_CLIP_MIXER
Chris@397 138 cerr << "ClipMixer::mix: have " << m_playing.size() << " playing note(s)"
Chris@397 139 << " and " << endingNotes.size() << " note(s) ending here"
Chris@397 140 << endl;
Chris@397 141 #endif
Chris@397 142
Chris@310 143 foreach (NoteStart note, m_playing) {
Chris@310 144
Chris@310 145 for (int c = 0; c < m_channels; ++c) {
Chris@345 146 levels[c] = note.level * gain;
Chris@310 147 }
Chris@310 148 if (note.pan != 0.0 && m_channels == 2) {
Chris@310 149 levels[0] *= 1.0 - note.pan;
Chris@310 150 levels[1] *= note.pan + 1.0;
Chris@310 151 }
Chris@310 152
Chris@310 153 int start = note.frameOffset;
Chris@310 154 int durationHere = m_blockSize;
Chris@310 155 if (start > 0) durationHere = m_blockSize - start;
Chris@310 156
Chris@310 157 bool ending = false;
Chris@310 158
Chris@310 159 foreach (NoteEnd end, endingNotes) {
Chris@310 160 if (end.frequency == note.frequency &&
Chris@310 161 end.frameOffset >= start &&
Chris@310 162 end.frameOffset <= m_blockSize) {
Chris@310 163 ending = true;
Chris@310 164 durationHere = end.frameOffset;
Chris@310 165 if (start > 0) durationHere = end.frameOffset - start;
Chris@310 166 break;
Chris@310 167 }
Chris@310 168 }
Chris@310 169
Chris@310 170 int clipDuration = getResampledClipDuration(note.frequency);
Chris@310 171 if (start + clipDuration > 0) {
Chris@310 172 if (start < 0 && start + clipDuration < durationHere) {
Chris@311 173 durationHere = start + clipDuration;
Chris@310 174 }
Chris@310 175 if (durationHere > 0) {
Chris@310 176 mixNote(toBuffers,
Chris@310 177 levels,
Chris@310 178 note.frequency,
Chris@310 179 start < 0 ? -start : 0,
Chris@310 180 start > 0 ? start : 0,
matthiasm@320 181 durationHere,
matthiasm@320 182 ending);
Chris@310 183 }
Chris@310 184 }
Chris@310 185
Chris@310 186 if (!ending) {
Chris@310 187 NoteStart adjusted = note;
Chris@310 188 adjusted.frameOffset -= m_blockSize;
Chris@310 189 remaining.push_back(adjusted);
Chris@310 190 }
Chris@310 191 }
Chris@310 192
Chris@313 193 delete[] levels;
Chris@313 194
Chris@310 195 m_playing = remaining;
Chris@307 196 }
Chris@307 197
Chris@310 198 void
Chris@310 199 ClipMixer::mixNote(float **toBuffers,
Chris@310 200 float *levels,
Chris@310 201 float frequency,
Chris@310 202 int sourceOffset,
Chris@310 203 int targetOffset,
matthiasm@320 204 int sampleCount,
matthiasm@320 205 bool isEnd)
Chris@310 206 {
Chris@310 207 if (!m_clipData) return;
Chris@310 208
Chris@310 209 float ratio = getResampleRatioFor(frequency);
Chris@310 210
matthiasm@320 211 float releaseTime = 0.01;
matthiasm@320 212 int releaseSampleCount = round(releaseTime * m_sampleRate);
matthiasm@320 213 if (releaseSampleCount > sampleCount) {
matthiasm@320 214 releaseSampleCount = sampleCount;
matthiasm@320 215 }
matthiasm@320 216 float releaseFraction = 1.f/releaseSampleCount;
Chris@310 217
Chris@310 218 for (int i = 0; i < sampleCount; ++i) {
Chris@310 219
Chris@310 220 int s = sourceOffset + i;
Chris@310 221
Chris@310 222 float os = s / ratio;
Chris@310 223 int osi = int(floor(os));
Chris@310 224
Chris@310 225 //!!! just linear interpolation for now (same as SV's sample
Chris@310 226 //!!! player). a small sinc kernel would be better and
Chris@310 227 //!!! probably "good enough"
Chris@310 228 float value = 0.f;
Chris@310 229 if (osi < m_clipLength) {
Chris@310 230 value += m_clipData[osi];
Chris@310 231 }
Chris@310 232 if (osi + 1 < m_clipLength) {
Chris@310 233 value += (m_clipData[osi + 1] - m_clipData[osi]) * (os - osi);
Chris@310 234 }
matthiasm@320 235
matthiasm@320 236 if (isEnd && i + releaseSampleCount > sampleCount) {
matthiasm@320 237 value *= releaseFraction * (sampleCount - i); // linear ramp for release
matthiasm@320 238 }
matthiasm@320 239
Chris@310 240 for (int c = 0; c < m_channels; ++c) {
Chris@311 241 toBuffers[c][targetOffset + i] += levels[c] * value;
Chris@310 242 }
Chris@310 243 }
Chris@310 244 }
Chris@310 245
Chris@310 246