annotate audio/ClipMixer.cpp @ 716:604393795ee5

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