annotate audioio/ClipMixer.cpp @ 403:eb84b06301da

Restore the old prev/next layer commands (that were never enabled because they didn't work) using the new fixed order layer list (so they now do work)
author Chris Cannam
date Tue, 02 Sep 2014 16:06:41 +0100
parents f747be6743ab
children b0b49da13975
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@305 27 m_clipData(0)
Chris@305 28 {
Chris@305 29 }
Chris@305 30
Chris@305 31 ClipMixer::~ClipMixer()
Chris@305 32 {
Chris@317 33 if (m_clipData) free(m_clipData);
Chris@305 34 }
Chris@305 35
Chris@308 36 void
Chris@308 37 ClipMixer::setChannelCount(int channels)
Chris@308 38 {
Chris@308 39 m_channels = channels;
Chris@308 40 }
Chris@308 41
Chris@305 42 bool
Chris@349 43 ClipMixer::loadClipData(QString path, float f0, float level)
Chris@305 44 {
Chris@305 45 if (m_clipData) {
Chris@307 46 cerr << "ClipMixer::loadClipData: Already have clip loaded" << endl;
Chris@305 47 return false;
Chris@305 48 }
Chris@305 49
Chris@305 50 SF_INFO info;
Chris@305 51 SNDFILE *file;
Chris@305 52 float *tmpFrames;
Chris@366 53 int i;
Chris@305 54
Chris@305 55 info.format = 0;
Chris@305 56 file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
Chris@305 57 if (!file) {
Chris@308 58 cerr << "ClipMixer::loadClipData: Failed to open file path \""
Chris@308 59 << path << "\": " << sf_strerror(file) << endl;
Chris@305 60 return false;
Chris@305 61 }
Chris@305 62
Chris@305 63 tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float));
Chris@305 64 if (!tmpFrames) {
Chris@307 65 cerr << "ClipMixer::loadClipData: malloc(" << info.frames * info.channels * sizeof(float) << ") failed" << endl;
Chris@305 66 return false;
Chris@305 67 }
Chris@305 68
Chris@305 69 sf_readf_float(file, tmpFrames, info.frames);
Chris@305 70 sf_close(file);
Chris@305 71
Chris@305 72 m_clipData = (float *)malloc(info.frames * sizeof(float));
Chris@305 73 if (!m_clipData) {
Chris@307 74 cerr << "ClipMixer::loadClipData: malloc(" << info.frames * sizeof(float) << ") failed" << endl;
Chris@305 75 free(tmpFrames);
Chris@305 76 return false;
Chris@305 77 }
Chris@305 78
Chris@305 79 for (i = 0; i < info.frames; ++i) {
Chris@305 80 int j;
Chris@305 81 m_clipData[i] = 0.0f;
Chris@305 82 for (j = 0; j < info.channels; ++j) {
Chris@349 83 m_clipData[i] += tmpFrames[i * info.channels + j] * level;
Chris@305 84 }
Chris@305 85 }
Chris@305 86
Chris@305 87 free(tmpFrames);
Chris@305 88
Chris@305 89 m_clipLength = info.frames;
Chris@305 90 m_clipF0 = f0;
Chris@305 91 m_clipRate = info.samplerate;
Chris@310 92
Chris@310 93 return true;
Chris@305 94 }
Chris@307 95
Chris@307 96 void
Chris@308 97 ClipMixer::reset()
Chris@308 98 {
Chris@310 99 m_playing.clear();
Chris@310 100 }
Chris@310 101
Chris@310 102 float
Chris@310 103 ClipMixer::getResampleRatioFor(float frequency)
Chris@310 104 {
Chris@310 105 if (!m_clipData) return 1.0;
Chris@311 106 float pitchRatio = m_clipF0 / frequency;
Chris@310 107 float resampleRatio = m_sampleRate / m_clipRate;
Chris@310 108 return pitchRatio * resampleRatio;
Chris@310 109 }
Chris@310 110
Chris@310 111 int
Chris@310 112 ClipMixer::getResampledClipDuration(float frequency)
Chris@310 113 {
Chris@310 114 return int(ceil(m_clipLength * getResampleRatioFor(frequency)));
Chris@308 115 }
Chris@308 116
Chris@308 117 void
Chris@307 118 ClipMixer::mix(float **toBuffers,
Chris@308 119 float gain,
Chris@307 120 std::vector<NoteStart> newNotes,
Chris@307 121 std::vector<NoteEnd> endingNotes)
Chris@307 122 {
Chris@310 123 foreach (NoteStart note, newNotes) {
matthiasm@321 124 if (note.frequency > 20 &&
matthiasm@322 125 note.frequency < 5000) {
matthiasm@321 126 m_playing.push_back(note);
matthiasm@321 127 }
Chris@310 128 }
Chris@310 129
Chris@310 130 std::vector<NoteStart> remaining;
Chris@310 131
Chris@310 132 float *levels = new float[m_channels];
Chris@310 133
Chris@397 134 #ifdef DEBUG_CLIP_MIXER
Chris@397 135 cerr << "ClipMixer::mix: have " << m_playing.size() << " playing note(s)"
Chris@397 136 << " and " << endingNotes.size() << " note(s) ending here"
Chris@397 137 << endl;
Chris@397 138 #endif
Chris@397 139
Chris@310 140 foreach (NoteStart note, m_playing) {
Chris@310 141
Chris@310 142 for (int c = 0; c < m_channels; ++c) {
Chris@345 143 levels[c] = note.level * gain;
Chris@310 144 }
Chris@310 145 if (note.pan != 0.0 && m_channels == 2) {
Chris@310 146 levels[0] *= 1.0 - note.pan;
Chris@310 147 levels[1] *= note.pan + 1.0;
Chris@310 148 }
Chris@310 149
Chris@310 150 int start = note.frameOffset;
Chris@310 151 int durationHere = m_blockSize;
Chris@310 152 if (start > 0) durationHere = m_blockSize - start;
Chris@310 153
Chris@310 154 bool ending = false;
Chris@310 155
Chris@310 156 foreach (NoteEnd end, endingNotes) {
Chris@310 157 if (end.frequency == note.frequency &&
Chris@310 158 end.frameOffset >= start &&
Chris@310 159 end.frameOffset <= m_blockSize) {
Chris@310 160 ending = true;
Chris@310 161 durationHere = end.frameOffset;
Chris@310 162 if (start > 0) durationHere = end.frameOffset - start;
Chris@310 163 break;
Chris@310 164 }
Chris@310 165 }
Chris@310 166
Chris@310 167 int clipDuration = getResampledClipDuration(note.frequency);
Chris@310 168 if (start + clipDuration > 0) {
Chris@310 169 if (start < 0 && start + clipDuration < durationHere) {
Chris@311 170 durationHere = start + clipDuration;
Chris@310 171 }
Chris@310 172 if (durationHere > 0) {
Chris@310 173 mixNote(toBuffers,
Chris@310 174 levels,
Chris@310 175 note.frequency,
Chris@310 176 start < 0 ? -start : 0,
Chris@310 177 start > 0 ? start : 0,
matthiasm@320 178 durationHere,
matthiasm@320 179 ending);
Chris@310 180 }
Chris@310 181 }
Chris@310 182
Chris@310 183 if (!ending) {
Chris@310 184 NoteStart adjusted = note;
Chris@310 185 adjusted.frameOffset -= m_blockSize;
Chris@310 186 remaining.push_back(adjusted);
Chris@310 187 }
Chris@310 188 }
Chris@310 189
Chris@313 190 delete[] levels;
Chris@313 191
Chris@310 192 m_playing = remaining;
Chris@307 193 }
Chris@307 194
Chris@310 195 void
Chris@310 196 ClipMixer::mixNote(float **toBuffers,
Chris@310 197 float *levels,
Chris@310 198 float frequency,
Chris@310 199 int sourceOffset,
Chris@310 200 int targetOffset,
matthiasm@320 201 int sampleCount,
matthiasm@320 202 bool isEnd)
Chris@310 203 {
Chris@310 204 if (!m_clipData) return;
Chris@310 205
Chris@310 206 float ratio = getResampleRatioFor(frequency);
Chris@310 207
matthiasm@320 208 float releaseTime = 0.01;
matthiasm@320 209 int releaseSampleCount = round(releaseTime * m_sampleRate);
matthiasm@320 210 if (releaseSampleCount > sampleCount) {
matthiasm@320 211 releaseSampleCount = sampleCount;
matthiasm@320 212 }
matthiasm@320 213 float releaseFraction = 1.f/releaseSampleCount;
Chris@310 214
Chris@310 215 for (int i = 0; i < sampleCount; ++i) {
Chris@310 216
Chris@310 217 int s = sourceOffset + i;
Chris@310 218
Chris@310 219 float os = s / ratio;
Chris@310 220 int osi = int(floor(os));
Chris@310 221
Chris@310 222 //!!! just linear interpolation for now (same as SV's sample
Chris@310 223 //!!! player). a small sinc kernel would be better and
Chris@310 224 //!!! probably "good enough"
Chris@310 225 float value = 0.f;
Chris@310 226 if (osi < m_clipLength) {
Chris@310 227 value += m_clipData[osi];
Chris@310 228 }
Chris@310 229 if (osi + 1 < m_clipLength) {
Chris@310 230 value += (m_clipData[osi + 1] - m_clipData[osi]) * (os - osi);
Chris@310 231 }
matthiasm@320 232
matthiasm@320 233 if (isEnd && i + releaseSampleCount > sampleCount) {
matthiasm@320 234 value *= releaseFraction * (sampleCount - i); // linear ramp for release
matthiasm@320 235 }
matthiasm@320 236
Chris@310 237 for (int c = 0; c < m_channels; ++c) {
Chris@311 238 toBuffers[c][targetOffset + i] += levels[c] * value;
Chris@310 239 }
Chris@310 240 }
Chris@310 241 }
Chris@310 242
Chris@310 243