Chris@23: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ matthiasm@0: Chris@35: /* Chris@35: NNLS-Chroma / Chordino Chris@35: Chris@35: Audio feature extraction plugins for chromagram and chord Chris@35: estimation. Chris@35: Chris@35: Centre for Digital Music, Queen Mary University of London. Chris@35: This file copyright 2008-2010 Matthias Mauch and QMUL. Chris@35: Chris@35: This program is free software; you can redistribute it and/or Chris@35: modify it under the terms of the GNU General Public License as Chris@35: published by the Free Software Foundation; either version 2 of the Chris@35: License, or (at your option) any later version. See the file Chris@35: COPYING included with this distribution for more information. Chris@35: */ Chris@35: matthiasm@0: #include "NNLSChroma.h" Chris@27: Chris@27: #include "chromamethods.h" Chris@27: Chris@27: #include Chris@27: #include matthiasm@0: #include matthiasm@9: Chris@27: #include matthiasm@0: matthiasm@0: const bool debug_on = false; matthiasm@0: matthiasm@0: NNLSChroma::NNLSChroma(float inputSampleRate) : Chris@35: NNLSBase(inputSampleRate) matthiasm@0: { Chris@23: if (debug_on) cerr << "--> NNLSChroma" << endl; matthiasm@0: } matthiasm@0: matthiasm@0: NNLSChroma::~NNLSChroma() matthiasm@0: { Chris@23: if (debug_on) cerr << "--> ~NNLSChroma" << endl; matthiasm@0: } matthiasm@0: matthiasm@0: string matthiasm@0: NNLSChroma::getIdentifier() const matthiasm@0: { Chris@23: if (debug_on) cerr << "--> getIdentifier" << endl; matthiasm@46: return "nnls-chroma"; matthiasm@0: } matthiasm@0: matthiasm@0: string matthiasm@0: NNLSChroma::getName() const matthiasm@0: { Chris@23: if (debug_on) cerr << "--> getName" << endl; matthiasm@0: return "NNLS Chroma"; matthiasm@0: } matthiasm@0: matthiasm@0: string matthiasm@0: NNLSChroma::getDescription() const matthiasm@0: { Chris@23: if (debug_on) cerr << "--> getDescription" << endl; matthiasm@58: return "This plugin provides a number of features derived from a DFT-based log-frequency amplitude spectrum: some variants of the log-frequency spectrum, including a semitone spectrum derived from approximate transcription using the NNLS algorithm; and based on this semitone spectrum, different chroma features."; matthiasm@0: } matthiasm@0: matthiasm@0: NNLSChroma::OutputList matthiasm@0: NNLSChroma::getOutputDescriptors() const matthiasm@0: { Chris@23: if (debug_on) cerr << "--> getOutputDescriptors" << endl; matthiasm@0: OutputList list; matthiasm@0: matthiasm@0: // Make chroma names for the binNames property matthiasm@120: matthiasm@120: const char* notenames[24] = { matthiasm@120: "A (bass)","Bb (bass)","B (bass)","C (bass)","C# (bass)","D (bass)","Eb (bass)","E (bass)","F (bass)","F# (bass)","G (bass)","Ab (bass)", matthiasm@120: "A","Bb","B","C","C#","D","Eb","E","F","F#","G","Ab"}; matthiasm@120: matthiasm@120: matthiasm@0: vector chromanames; matthiasm@0: vector bothchromanames; matthiasm@0: for (int iNote = 0; iNote < 24; iNote++) { matthiasm@0: bothchromanames.push_back(notenames[iNote]); matthiasm@0: if (iNote < 12) { matthiasm@43: chromanames.push_back(notenames[iNote+12]); matthiasm@0: } matthiasm@0: } matthiasm@0: Chris@35: int index = 0; matthiasm@0: mail@117: OutputDescriptor logfreqspecOutput; mail@117: logfreqspecOutput.identifier = "logfreqspec"; mail@117: logfreqspecOutput.name = "Log-Frequency Spectrum"; mail@117: logfreqspecOutput.description = "A Log-Frequency Spectrum (constant Q) that is obtained by cosine filter mapping."; mail@117: logfreqspecOutput.unit = ""; mail@117: logfreqspecOutput.hasFixedBinCount = true; mail@117: logfreqspecOutput.binCount = nNote; mail@117: logfreqspecOutput.hasKnownExtents = false; mail@117: logfreqspecOutput.isQuantized = false; mail@117: logfreqspecOutput.sampleType = OutputDescriptor::FixedSampleRate; mail@117: logfreqspecOutput.hasDuration = false; mail@117: logfreqspecOutput.sampleRate = (m_stepSize == 0) ? m_inputSampleRate/2048 : m_inputSampleRate/m_stepSize; mail@117: list.push_back(logfreqspecOutput); mail@117: m_outputLogfreqspec = index++; matthiasm@0: mail@117: OutputDescriptor tunedlogfreqspecOutput; mail@117: tunedlogfreqspecOutput.identifier = "tunedlogfreqspec"; mail@117: tunedlogfreqspecOutput.name = "Tuned Log-Frequency Spectrum"; mail@117: tunedlogfreqspecOutput.description = "A Log-Frequency Spectrum (constant Q) that is obtained by cosine filter mapping, then its tuned using the estimated tuning frequency."; mail@117: tunedlogfreqspecOutput.unit = ""; mail@117: tunedlogfreqspecOutput.hasFixedBinCount = true; mail@117: tunedlogfreqspecOutput.binCount = nNote; mail@117: tunedlogfreqspecOutput.hasKnownExtents = false; mail@117: tunedlogfreqspecOutput.isQuantized = false; mail@117: tunedlogfreqspecOutput.sampleType = OutputDescriptor::FixedSampleRate; mail@117: tunedlogfreqspecOutput.hasDuration = false; mail@117: tunedlogfreqspecOutput.sampleRate = (m_stepSize == 0) ? m_inputSampleRate/2048 : m_inputSampleRate/m_stepSize; mail@117: list.push_back(tunedlogfreqspecOutput); mail@117: m_outputTunedlogfreqspec = index++; matthiasm@0: mail@117: OutputDescriptor semitonespectrumOutput; mail@117: semitonespectrumOutput.identifier = "semitonespectrum"; mail@117: semitonespectrumOutput.name = "Semitone Spectrum"; mail@117: semitonespectrumOutput.description = "A semitone-spaced log-frequency spectrum derived from the third-of-a-semitone-spaced tuned log-frequency spectrum."; mail@117: semitonespectrumOutput.unit = ""; mail@117: semitonespectrumOutput.hasFixedBinCount = true; mail@117: semitonespectrumOutput.binCount = 84; mail@117: semitonespectrumOutput.hasKnownExtents = false; mail@117: semitonespectrumOutput.isQuantized = false; mail@117: semitonespectrumOutput.sampleType = OutputDescriptor::FixedSampleRate; mail@117: semitonespectrumOutput.hasDuration = false; mail@117: semitonespectrumOutput.sampleRate = (m_stepSize == 0) ? m_inputSampleRate/2048 : m_inputSampleRate/m_stepSize; mail@117: list.push_back(semitonespectrumOutput); mail@117: m_outputSemitonespectrum = index++; matthiasm@0: mail@117: OutputDescriptor chromaOutput; mail@117: chromaOutput.identifier = "chroma"; mail@117: chromaOutput.name = "Chromagram"; mail@117: chromaOutput.description = "Tuning-adjusted chromagram from NNLS approximate transcription, with an emphasis on the medium note range."; mail@117: chromaOutput.unit = ""; mail@117: chromaOutput.hasFixedBinCount = true; mail@117: chromaOutput.binCount = 12; mail@117: chromaOutput.binNames = chromanames; mail@117: chromaOutput.hasKnownExtents = false; mail@117: chromaOutput.isQuantized = false; mail@117: chromaOutput.sampleType = OutputDescriptor::FixedSampleRate; mail@117: chromaOutput.hasDuration = false; mail@117: chromaOutput.sampleRate = (m_stepSize == 0) ? m_inputSampleRate/2048 : m_inputSampleRate/m_stepSize; mail@117: list.push_back(chromaOutput); Chris@35: m_outputChroma = index++; matthiasm@0: mail@116: OutputDescriptor basschromaOutput; mail@116: basschromaOutput.identifier = "basschroma"; mail@116: basschromaOutput.name = "Bass Chromagram"; mail@116: basschromaOutput.description = "Tuning-adjusted bass chromagram from NNLS approximate transcription, with an emphasis on the bass note range."; mail@116: basschromaOutput.unit = ""; mail@116: basschromaOutput.hasFixedBinCount = true; mail@116: basschromaOutput.binCount = 12; mail@116: basschromaOutput.binNames = chromanames; mail@116: basschromaOutput.hasKnownExtents = false; mail@116: basschromaOutput.isQuantized = false; mail@116: basschromaOutput.sampleType = OutputDescriptor::FixedSampleRate; mail@116: basschromaOutput.hasDuration = false; mail@116: basschromaOutput.sampleRate = (m_stepSize == 0) ? m_inputSampleRate/2048 : m_inputSampleRate/m_stepSize; mail@116: list.push_back(basschromaOutput); mail@117: m_outputBasschroma = index++; matthiasm@0: mail@116: OutputDescriptor bothchromaOutput; mail@116: bothchromaOutput.identifier = "bothchroma"; mail@116: bothchromaOutput.name = "Chromagram and Bass Chromagram"; mail@116: bothchromaOutput.description = "Tuning-adjusted chromagram and bass chromagram (stacked on top of each other) from NNLS approximate transcription."; mail@116: bothchromaOutput.unit = ""; mail@116: bothchromaOutput.hasFixedBinCount = true; mail@116: bothchromaOutput.binCount = 24; mail@116: bothchromaOutput.binNames = bothchromanames; mail@116: bothchromaOutput.hasKnownExtents = false; mail@116: bothchromaOutput.isQuantized = false; mail@116: bothchromaOutput.sampleType = OutputDescriptor::FixedSampleRate; mail@116: bothchromaOutput.hasDuration = false; mail@116: bothchromaOutput.sampleRate = (m_stepSize == 0) ? m_inputSampleRate/2048 : m_inputSampleRate/m_stepSize; mail@116: list.push_back(bothchromaOutput); matthiasm@129: m_outputBothchroma = index++; matthiasm@0: return list; matthiasm@0: } matthiasm@0: matthiasm@0: matthiasm@0: bool matthiasm@0: NNLSChroma::initialise(size_t channels, size_t stepSize, size_t blockSize) matthiasm@0: { Chris@23: if (debug_on) { Chris@23: cerr << "--> initialise"; Chris@23: } matthiasm@1: Chris@35: if (!NNLSBase::initialise(channels, stepSize, blockSize)) { Chris@35: return false; Chris@35: } matthiasm@1: matthiasm@0: return true; matthiasm@0: } matthiasm@0: matthiasm@0: void matthiasm@0: NNLSChroma::reset() matthiasm@0: { Chris@23: if (debug_on) cerr << "--> reset"; Chris@35: NNLSBase::reset(); matthiasm@0: } matthiasm@0: matthiasm@0: NNLSChroma::FeatureSet matthiasm@0: NNLSChroma::process(const float *const *inputBuffers, Vamp::RealTime timestamp) matthiasm@0: { Chris@23: if (debug_on) cerr << "--> process" << endl; Chris@35: Chris@35: NNLSBase::baseProcess(inputBuffers, timestamp); matthiasm@0: Chris@23: FeatureSet fs; mail@117: fs[m_outputLogfreqspec].push_back(m_logSpectrum[m_logSpectrum.size()-1]); Chris@23: return fs; matthiasm@0: } matthiasm@0: matthiasm@0: NNLSChroma::FeatureSet matthiasm@0: NNLSChroma::getRemainingFeatures() matthiasm@0: { mail@119: mail@119: if (debug_on) cerr << "--> getRemainingFeatures" << endl; Chris@23: FeatureSet fsOut; Chris@35: if (m_logSpectrum.size() == 0) return fsOut; mail@119: Chris@23: /** Calculate Tuning Chris@23: calculate tuning from (using the angle of the complex number defined by the Chris@23: cumulative mean real and imag values) Chris@23: **/ mail@80: float meanTuningImag = 0; mail@80: float meanTuningReal = 0; mail@80: for (int iBPS = 0; iBPS < nBPS; ++iBPS) { mail@80: meanTuningReal += m_meanTunings[iBPS] * cosvalues[iBPS]; mail@80: meanTuningImag += m_meanTunings[iBPS] * sinvalues[iBPS]; mail@80: } Chris@23: float cumulativetuning = 440 * pow(2,atan2(meanTuningImag, meanTuningReal)/(24*M_PI)); Chris@23: float normalisedtuning = atan2(meanTuningImag, meanTuningReal)/(2*M_PI); Chris@23: int intShift = floor(normalisedtuning * 3); mail@80: float floatShift = normalisedtuning * 3 - intShift; // floatShift is a really bad name for this matthiasm@1: Chris@23: char buffer0 [50]; matthiasm@1: Chris@23: sprintf(buffer0, "estimated tuning: %0.1f Hz", cumulativetuning); mail@119: Chris@23: /** Tune Log-Frequency Spectrogram Chris@23: calculate a tuned log-frequency spectrogram (f2): use the tuning estimated above (kinda f0) to Chris@23: perform linear interpolation on the existing log-frequency spectrogram (kinda f1). Chris@23: **/ Chris@163: if (debug_on) cerr << endl << "[NNLS Chroma Plugin] Tuning Log-Frequency Spectrogram ... "; matthiasm@13: Chris@23: float tempValue = 0; matthiasm@120: Chris@23: int count = 0; mail@77: matthiasm@1: Chris@35: for (FeatureList::iterator i = m_logSpectrum.begin(); i != m_logSpectrum.end(); ++i) { Chris@23: Feature f1 = *i; Chris@23: Feature f2; // tuned log-frequency spectrum Chris@23: f2.hasTimestamp = true; Chris@23: f2.timestamp = f1.timestamp; Chris@23: f2.values.push_back(0.0); f2.values.push_back(0.0); // set lower edge to zero matthiasm@1: matthiasm@85: Chris@23: if (m_tuneLocal) { Chris@23: intShift = floor(m_localTuning[count] * 3); mail@80: floatShift = m_localTuning[count] * 3 - intShift; // floatShift is a really bad name for this Chris@23: } matthiasm@1: mail@80: // cerr << intShift << " " << floatShift << endl; matthiasm@1: matthiasm@122: for (int k = 2; k < (int)f1.values.size() - 3; ++k) { // interpolate all inner bins mail@80: tempValue = f1.values[k + intShift] * (1-floatShift) + f1.values[k+intShift+1] * floatShift; Chris@23: f2.values.push_back(tempValue); Chris@23: } matthiasm@1: Chris@23: f2.values.push_back(0.0); f2.values.push_back(0.0); f2.values.push_back(0.0); // upper edge mail@77: Chris@23: vector runningmean = SpecialConvolution(f2.values,hw); Chris@23: vector runningstd; mail@77: for (int i = 0; i < nNote; i++) { // first step: squared values into vector (variance) Chris@23: runningstd.push_back((f2.values[i] - runningmean[i]) * (f2.values[i] - runningmean[i])); Chris@23: } Chris@23: runningstd = SpecialConvolution(runningstd,hw); // second step convolve mail@77: for (int i = 0; i < nNote; i++) { Chris@23: runningstd[i] = sqrt(runningstd[i]); // square root to finally have running std Chris@23: if (runningstd[i] > 0) { Chris@23: // f2.values[i] = (f2.values[i] / runningmean[i]) > thresh ? mail@41: // (f2.values[i] - runningmean[i]) / pow(runningstd[i],m_whitening) : 0; Chris@23: f2.values[i] = (f2.values[i] - runningmean[i]) > 0 ? mail@41: (f2.values[i] - runningmean[i]) / pow(runningstd[i],m_whitening) : 0; Chris@23: } Chris@23: if (f2.values[i] < 0) { Chris@23: cerr << "ERROR: negative value in logfreq spectrum" << endl; Chris@23: } Chris@23: } mail@117: fsOut[m_outputTunedlogfreqspec].push_back(f2); Chris@23: count++; Chris@23: } Chris@163: if (debug_on) cerr << "done." << endl; matthiasm@1: Chris@23: /** Semitone spectrum and chromagrams Chris@23: Semitone-spaced log-frequency spectrum derived from the tuned log-freq spectrum above. the spectrum Chris@23: is inferred using a non-negative least squares algorithm. Chris@23: Three different kinds of chromagram are calculated, "treble", "bass", and "both" (which means Chris@23: bass and treble stacked onto each other). Chris@23: **/ matthiasm@42: if (m_useNNLS == 0) { Chris@163: if (debug_on) cerr << "[NNLS Chroma Plugin] Mapping to semitone spectrum and chroma ... "; Chris@23: } else { Chris@163: if (debug_on) cerr << "[NNLS Chroma Plugin] Performing NNLS and mapping to chroma ... "; Chris@23: } matthiasm@13: matthiasm@1: Chris@23: vector oldchroma = vector(12,0); Chris@23: vector oldbasschroma = vector(12,0); Chris@23: count = 0; matthiasm@9: mail@117: for (FeatureList::iterator it = fsOut[m_outputTunedlogfreqspec].begin(); it != fsOut[m_outputTunedlogfreqspec].end(); ++it) { Chris@23: Feature f2 = *it; // logfreq spectrum Chris@23: Feature f3; // semitone spectrum Chris@23: Feature f4; // treble chromagram Chris@23: Feature f5; // bass chromagram Chris@23: Feature f6; // treble and bass chromagram matthiasm@85: Chris@23: f3.hasTimestamp = true; Chris@23: f3.timestamp = f2.timestamp; matthiasm@1: Chris@23: f4.hasTimestamp = true; Chris@23: f4.timestamp = f2.timestamp; matthiasm@1: Chris@23: f5.hasTimestamp = true; Chris@23: f5.timestamp = f2.timestamp; matthiasm@1: Chris@23: f6.hasTimestamp = true; Chris@23: f6.timestamp = f2.timestamp; matthiasm@1: mail@77: float b[nNote]; matthiasm@1: Chris@23: bool some_b_greater_zero = false; Chris@23: float sumb = 0; mail@77: for (int i = 0; i < nNote; i++) { mail@77: // b[i] = m_dict[(nNote * count + i) % (nNote * 84)]; Chris@23: b[i] = f2.values[i]; Chris@23: sumb += b[i]; Chris@23: if (b[i] > 0) { Chris@23: some_b_greater_zero = true; Chris@23: } Chris@23: } matthiasm@1: Chris@23: // here's where the non-negative least squares algorithm calculates the note activation x matthiasm@1: Chris@23: vector chroma = vector(12, 0); Chris@23: vector basschroma = vector(12, 0); Chris@23: float currval; matthiasm@122: int iSemitone = 0; matthiasm@1: Chris@23: if (some_b_greater_zero) { matthiasm@42: if (m_useNNLS == 0) { matthiasm@122: for (int iNote = nBPS/2 + 2; iNote < nNote - nBPS/2; iNote += nBPS) { Chris@23: currval = 0; mail@80: for (int iBPS = -nBPS/2; iBPS < nBPS/2+1; ++iBPS) { mail@80: currval += b[iNote + iBPS] * (1-abs(iBPS*1.0/(nBPS/2+1))); mail@80: } Chris@23: f3.values.push_back(currval); Chris@23: chroma[iSemitone % 12] += currval * treblewindow[iSemitone]; Chris@23: basschroma[iSemitone % 12] += currval * basswindow[iSemitone]; Chris@23: iSemitone++; Chris@23: } matthiasm@1: Chris@23: } else { Chris@35: float x[84+1000]; Chris@23: for (int i = 1; i < 1084; ++i) x[i] = 1.0; Chris@23: vector signifIndex; Chris@23: int index=0; Chris@23: sumb /= 84.0; matthiasm@122: for (int iNote = nBPS/2 + 2; iNote < nNote - nBPS/2; iNote += nBPS) { Chris@23: float currval = 0; mail@80: for (int iBPS = -nBPS/2; iBPS < nBPS/2+1; ++iBPS) { mail@80: currval += b[iNote + iBPS]; mail@80: } Chris@23: if (currval > 0) signifIndex.push_back(index); Chris@23: f3.values.push_back(0); // fill the values, change later Chris@23: index++; Chris@23: } Chris@35: float rnorm; Chris@35: float w[84+1000]; Chris@35: float zz[84+1000]; Chris@23: int indx[84+1000]; Chris@23: int mode; mail@77: int dictsize = nNote*signifIndex.size(); Chris@23: // cerr << "dictsize is " << dictsize << "and values size" << f3.values.size()<< endl; Chris@35: float *curr_dict = new float[dictsize]; Chris@91: for (int iNote = 0; iNote < (int)signifIndex.size(); ++iNote) { Chris@91: for (int iBin = 0; iBin < nNote; iBin++) { mail@77: curr_dict[iNote * nNote + iBin] = 1.0 * m_dict[signifIndex[iNote] * nNote + iBin]; Chris@23: } Chris@23: } Chris@35: nnls(curr_dict, nNote, nNote, signifIndex.size(), b, x, &rnorm, w, zz, indx, &mode); Chris@23: delete [] curr_dict; Chris@91: for (int iNote = 0; iNote < (int)signifIndex.size(); ++iNote) { Chris@23: f3.values[signifIndex[iNote]] = x[iNote]; Chris@23: // cerr << mode << endl; Chris@23: chroma[signifIndex[iNote] % 12] += x[iNote] * treblewindow[signifIndex[iNote]]; Chris@23: basschroma[signifIndex[iNote] % 12] += x[iNote] * basswindow[signifIndex[iNote]]; Chris@23: } Chris@23: } matthiasm@79: } else { matthiasm@79: for (int i = 0; i < 84; ++i) f3.values.push_back(0); Chris@23: } matthiasm@85: matthiasm@129: Chris@23: f4.values = chroma; Chris@23: f5.values = basschroma; Chris@23: chroma.insert(chroma.begin(), basschroma.begin(), basschroma.end()); // just stack the both chromas Chris@23: f6.values = chroma; matthiasm@1: Chris@23: if (m_doNormalizeChroma > 0) { Chris@23: vector chromanorm = vector(3,0); Chris@23: switch (int(m_doNormalizeChroma)) { Chris@23: case 0: // should never end up here Chris@23: break; Chris@23: case 1: Chris@23: chromanorm[0] = *max_element(f4.values.begin(), f4.values.end()); Chris@23: chromanorm[1] = *max_element(f5.values.begin(), f5.values.end()); Chris@23: chromanorm[2] = max(chromanorm[0], chromanorm[1]); Chris@23: break; Chris@23: case 2: Chris@23: for (vector::iterator it = f4.values.begin(); it != f4.values.end(); ++it) { Chris@23: chromanorm[0] += *it; Chris@23: } Chris@23: for (vector::iterator it = f5.values.begin(); it != f5.values.end(); ++it) { Chris@23: chromanorm[1] += *it; Chris@23: } Chris@23: for (vector::iterator it = f6.values.begin(); it != f6.values.end(); ++it) { Chris@23: chromanorm[2] += *it; Chris@23: } Chris@23: break; Chris@23: case 3: Chris@23: for (vector::iterator it = f4.values.begin(); it != f4.values.end(); ++it) { Chris@23: chromanorm[0] += pow(*it,2); Chris@23: } Chris@23: chromanorm[0] = sqrt(chromanorm[0]); Chris@23: for (vector::iterator it = f5.values.begin(); it != f5.values.end(); ++it) { Chris@23: chromanorm[1] += pow(*it,2); Chris@23: } Chris@23: chromanorm[1] = sqrt(chromanorm[1]); Chris@23: for (vector::iterator it = f6.values.begin(); it != f6.values.end(); ++it) { Chris@23: chromanorm[2] += pow(*it,2); Chris@23: } Chris@23: chromanorm[2] = sqrt(chromanorm[2]); Chris@23: break; Chris@23: } Chris@23: if (chromanorm[0] > 0) { matthiasm@122: for (int i = 0; i < (int)f4.values.size(); i++) { Chris@23: f4.values[i] /= chromanorm[0]; Chris@23: } Chris@23: } Chris@23: if (chromanorm[1] > 0) { matthiasm@122: for (int i = 0; i < (int)f5.values.size(); i++) { Chris@23: f5.values[i] /= chromanorm[1]; Chris@23: } Chris@23: } Chris@23: if (chromanorm[2] > 0) { matthiasm@122: for (int i = 0; i < (int)f6.values.size(); i++) { Chris@23: f6.values[i] /= chromanorm[2]; Chris@23: } Chris@23: } Chris@23: } matthiasm@13: mail@117: fsOut[m_outputSemitonespectrum].push_back(f3); Chris@35: fsOut[m_outputChroma].push_back(f4); mail@117: fsOut[m_outputBasschroma].push_back(f5); mail@117: fsOut[m_outputBothchroma].push_back(f6); Chris@23: count++; Chris@23: } Chris@163: if (debug_on) cerr << "done." << endl; matthiasm@10: Chris@23: return fsOut; matthiasm@0: matthiasm@0: } matthiasm@0: