Mercurial > hg > svapp
changeset 310:8c59cc68eabd tonioni
Initial implementation of clip note mix
author | Chris Cannam |
---|---|
date | Tue, 07 Jan 2014 17:11:14 +0000 |
parents | 71050ffd0141 |
children | 86fb9160d122 |
files | audioio/ClipMixer.cpp audioio/ClipMixer.h |
diffstat | 2 files changed, 130 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/audioio/ClipMixer.cpp Tue Jan 07 15:52:47 2014 +0000 +++ b/audioio/ClipMixer.cpp Tue Jan 07 17:11:14 2014 +0000 @@ -16,6 +16,7 @@ #include "ClipMixer.h" #include <sndfile.h> +#include <cmath> #include "base/Debug.h" @@ -89,12 +90,29 @@ m_clipLength = info.frames; m_clipF0 = f0; m_clipRate = info.samplerate; + + return true; } void ClipMixer::reset() { - //!!! + m_playing.clear(); +} + +float +ClipMixer::getResampleRatioFor(float frequency) +{ + if (!m_clipData) return 1.0; + float pitchRatio = frequency / m_clipF0; + float resampleRatio = m_sampleRate / m_clipRate; + return pitchRatio * resampleRatio; +} + +int +ClipMixer::getResampledClipDuration(float frequency) +{ + return int(ceil(m_clipLength * getResampleRatioFor(frequency))); } void @@ -103,6 +121,102 @@ std::vector<NoteStart> newNotes, std::vector<NoteEnd> endingNotes) { - //!!! do this! + foreach (NoteStart note, newNotes) { + m_playing.push_back(note); + } + + std::vector<NoteStart> remaining; + + float *levels = new float[m_channels]; + + foreach (NoteStart note, m_playing) { + + for (int c = 0; c < m_channels; ++c) { + levels[c] = gain; + } + if (note.pan != 0.0 && m_channels == 2) { + levels[0] *= 1.0 - note.pan; + levels[1] *= note.pan + 1.0; + } + + int start = note.frameOffset; + int durationHere = m_blockSize; + if (start > 0) durationHere = m_blockSize - start; + + bool ending = false; + + foreach (NoteEnd end, endingNotes) { + if (end.frequency == note.frequency && + end.frameOffset >= start && + end.frameOffset <= m_blockSize) { + ending = true; + durationHere = end.frameOffset; + if (start > 0) durationHere = end.frameOffset - start; + break; + } + } + + int clipDuration = getResampledClipDuration(note.frequency); + if (start + clipDuration > 0) { + if (start < 0 && start + clipDuration < durationHere) { + durationHere = clipDuration - start; + } + if (durationHere > 0) { + mixNote(toBuffers, + levels, + note.frequency, + start < 0 ? -start : 0, + start > 0 ? start : 0, + durationHere); + } + } + + if (!ending) { + NoteStart adjusted = note; + adjusted.frameOffset -= m_blockSize; + remaining.push_back(adjusted); + } + } + + m_playing = remaining; } +void +ClipMixer::mixNote(float **toBuffers, + float *levels, + float frequency, + int sourceOffset, + int targetOffset, + int sampleCount) +{ + if (!m_clipData) return; + + float ratio = getResampleRatioFor(frequency); + + //!!! todo: release time + + for (int i = 0; i < sampleCount; ++i) { + + int s = sourceOffset + i; + + float os = s / ratio; + int osi = int(floor(os)); + + //!!! just linear interpolation for now (same as SV's sample + //!!! player). a small sinc kernel would be better and + //!!! probably "good enough" + float value = 0.f; + if (osi < m_clipLength) { + value += m_clipData[osi]; + } + if (osi + 1 < m_clipLength) { + value += (m_clipData[osi + 1] - m_clipData[osi]) * (os - osi); + } + + for (int c = 0; c < m_channels; ++c) { + toBuffers[c][targetOffset + i] = levels[c] * value; + } + } +} + +
--- a/audioio/ClipMixer.h Tue Jan 07 15:52:47 2014 +0000 +++ b/audioio/ClipMixer.h Tue Jan 07 17:11:14 2014 +0000 @@ -40,15 +40,15 @@ //!!! AudioGenerator's NoteOff? struct NoteStart { - int frameOffset; // in current processing block + int frameOffset; // within current processing block float frequency; // Hz float level; // volume in range (0,1] float pan; // range [-1,1] }; struct NoteEnd { + int frameOffset; // in current processing block float frequency; // matching note start - int frameOffset; // in current processing block }; void mix(float **toBuffers, @@ -67,6 +67,18 @@ int m_clipLength; float m_clipF0; float m_clipRate; + + std::vector<NoteStart> m_playing; + + float getResampleRatioFor(float frequency); + int getResampledClipDuration(float frequency); + + void mixNote(float **toBuffers, + float *levels, + float frequency, + int sourceOffset, // within resampled note + int targetOffset, // within target buffer + int sampleCount); };