std::vector<typename Partial::SolutionT> generatePar(int depth, Partial const & part, Constraint constr) { using SolutionVec = std::vector<typename Partial::SolutionT>; if (depth == 0) { return generate(part, constr); } else if (part.isFinished(constr)) { SolutionVec result{ part.getSolution() }; return result; } else { Stream<Partial> partList = part.refine(constr); std::vector<std::future<SolutionVec>> futResult; forEach(std::move(partList), [&constr, &futResult, depth](Partial const & part) { std::future<SolutionVec> futLst = std::async([constr, part, depth]() { return generatePar(depth - 1, part, constr); }); futResult.push_back(std::move(futLst)); }); std::vector<SolutionVec> all = when_all_vec(futResult); return concatAll(all); } }
// --------------------------------------------------------------------------- // harmonify // --------------------------------------------------------------------------- //! Apply the reference envelope to a Partial. //! //! \pre The Partial p must be labeled with its harmonic number. // void Harmonifier::harmonify( Partial & p ) const { // compute absolute magnitude thresholds: static const double FadeRangeDB = 10; const double BeginFade = std::pow( 10., 0.05 * (_freqFixThresholdDb+FadeRangeDB) ); const double Threshold = std::pow( 10., 0.05 * _freqFixThresholdDb ); const double OneOverFadeSpan = 1. / ( BeginFade - Threshold ); double fscale = (double)p.label() / _refPartial.label(); for ( Partial::iterator it = p.begin(); it != p.end(); ++it ) { Breakpoint & bp = it.breakpoint(); if ( bp.amplitude() < BeginFade ) { // alpha is the harmonic frequency weighting: // when alpha is 1, the harmonic frequency is used, // when alpha is 0, the breakpoint frequency is // unmodified. double alpha = std::min( ( BeginFade - bp.amplitude() ) * OneOverFadeSpan, 1. ); // alpha is scaled by the weigthing envelope alpha *= _weight->valueAt( it.time() ); double fRef = _refPartial.frequencyAt( it.time() ); bp.setFrequency( ( alpha * ( fRef * fscale ) ) + ( (1 - alpha) * bp.frequency() ) ); } } }
// --------------------------------------------------------------------------- // BandwidthSetter function call operator // --------------------------------------------------------------------------- // Set the bandwidth of the specified Partial according to // an envelope representing a time-varying bandwidth value. // void BandwidthSetter::operator()( Partial & p ) const { for ( Partial::iterator pos = p.begin(); pos != p.end(); ++pos ) { pos.breakpoint().setBandwidth( env->valueAt( pos.time() ) ); } }
bool PartialBuilder::better_match( const Partial & part1, const Partial & part2, const SpectralPeak & pk ) { Assert( part1.numBreakpoints() > 0 ); Assert( part2.numBreakpoints() > 0 ); return freq_distance( part1, pk ) < freq_distance( part2, pk ); }
// --------------------------------------------------------------------------- // FrequencyScaler function call operator // --------------------------------------------------------------------------- // Scale the frequency of the specified Partial according to // an envelope representing a time-varying frequency scale value. // void FrequencyScaler::operator()( Partial & p ) const { for ( Partial::iterator pos = p.begin(); pos != p.end(); ++pos ) { pos.breakpoint().setFrequency( pos.breakpoint().frequency() * env->valueAt( pos.time() ) ); } }
// --------------------------------------------------------------------------- // AmplitudeScaler function call operator // --------------------------------------------------------------------------- // Scale the amplitude of the specified Partial according to // an envelope representing a time-varying amplitude scale value. // void AmplitudeScaler::operator()( Partial & p ) const { for ( Partial::iterator pos = p.begin(); pos != p.end(); ++pos ) { pos.breakpoint().setAmplitude( pos.breakpoint().amplitude() * env->valueAt( pos.time() ) ); } }
// --------------------------------------------------------------------------- // fixPhaseBefore // //! Recompute phases of all Breakpoints earlier than the specified time //! so that the synthesize phases of those earlier Breakpoints matches //! the stored phase, and the synthesized phase at the specified //! time matches the stored (not recomputed) phase. //! //! Backward phase-fixing stops if a null (zero-amplitude) Breakpoint //! is encountered, because nulls are interpreted as phase reset points //! in Loris. If a null is encountered, the remainder of the Partial //! (the front part) is fixed in the forward direction, beginning at //! the start of the Partial. //! //! \param p The Partial whose phases should be fixed. //! \param t The time before which phases should be adjusted. // void fixPhaseBefore( Partial & p, double t ) { if ( 1 < p.numBreakpoints() ) { Partial::iterator pos = p.findNearest( t ); Assert( pos != p.end() ); fixPhaseBackward( p.begin(), pos ); } }
// --------------------------------------------------------------------------- // freq_distance // --------------------------------------------------------------------------- // Helper function, used in formPartials(). // Returns the (positive) frequency distance between a Breakpoint // and the last Breakpoint in a Partial. // inline double PartialBuilder::freq_distance( const Partial & partial, const SpectralPeak & pk ) { double normBpFreq = pk.frequency() / mFreqWarping->valueAt( pk.time() ); double normPartialEndFreq = partial.last().frequency() / mFreqWarping->valueAt( partial.endTime() ); return std::fabs( normPartialEndFreq - normBpFreq ); }
// --------------------------------------------------------------------------- // PitchShifter function call operator // --------------------------------------------------------------------------- // Shift the pitch of the specified Partial according to // the given pitch envelope. The pitch envelope is assumed to have // units of cents (1/100 of a halfstep). // void PitchShifter::operator()( Partial & p ) const { for ( Partial::iterator pos = p.begin(); pos != p.end(); ++pos ) { // compute frequency scale: double scale = std::pow( 2., ( 0.01 * env->valueAt( pos.time() ) ) / 12. ); pos.breakpoint().setFrequency( pos.breakpoint().frequency() * scale ); } }
// --------------------------------------------------------------------------- // peakAmplitude // --------------------------------------------------------------------------- //! Return the maximum amplitude achieved by a partial. //! //! \param p is the Partial to evaluate //! \return the maximum (absolute) amplitude achieved by //! the partial p // double peakAmplitude( const Partial & p ) { double peak = 0; for ( Partial::const_iterator it = p.begin(); it != p.end(); ++it ) { peak = std::max( peak, it->amplitude() ); } return peak; }
// --------------------------------------------------------------------------- // fixPhaseAfter // //! Recompute phases of all Breakpoints later than the specified time //! so that the synthesize phases of those later Breakpoints matches //! the stored phase, as long as the synthesized phase at the specified //! time matches the stored (not recomputed) phase. //! //! Phase fixing is only applied to non-null (nonzero-amplitude) Breakpoints, //! because null Breakpoints are interpreted as phase reset points in //! Loris. If a null is encountered, its phase is simply left unmodified, //! and future phases wil be recomputed from that one. //! //! \param p The Partial whose phases should be fixed. //! \param t The time after which phases should be adjusted. // void fixPhaseAfter( Partial & p, double t ) { // nothing to do it there are not at least // two Breakpoints in the Partial if ( 1 < p.numBreakpoints() ) { Partial::iterator pos = p.findNearest( t ); Assert( pos != p.end() ); fixPhaseForward( pos, --p.end() ); } }
bool Poly::startAbort() { if (state == POLY_Inactive) { return false; } for (int t = 0; t < 4; t++) { Partial *partial = partials[t]; if (partial != NULL) { partial->startAbort(); } } return true; }
// --------------------------------------------------------------------------- // fixFrequency // //! Adjust frequencies of the Breakpoints in the //! specified Partial such that the rendered Partial //! achieves (or matches as nearly as possible, within //! the constraint of the maximum allowable frequency //! alteration) the analyzed phases. //! //! This just iterates over the Partial calling //! matchPhaseFwd, should probably name those similarly. //! //! \param partial The Partial whose frequencies, //! and possibly phases (if the frequencies //! cannot be sufficiently altered to match //! the phases), will be recomputed. //! \param maxFixPct The maximum allowable frequency //! alteration, default is 0.2%. // void fixFrequency( Partial & partial, double maxFixPct ) { if ( partial.numBreakpoints() > 1 ) { Partial::iterator next = partial.begin(); Partial::iterator prev = next++; while ( next != partial.end() ) { matchPhaseFwd( prev.breakpoint(), next.breakpoint(), next.time() - prev.time(), 0.5, maxFixPct ); prev = next++; } } }
bool Poly::startDecay() { if (state == POLY_Inactive || state == POLY_Releasing) { return false; } state = POLY_Releasing; for (int t = 0; t < 4; t++) { Partial *partial = partials[t]; if (partial != NULL) { partial->startDecayAll(); } } return true; }
// --------------------------------------------------------------------------- // channelize (one Partial) // --------------------------------------------------------------------------- //! Label a Partial with the number of the frequency channel corresponding to //! the average frequency over all the Partial's Breakpoints. //! //! \param partial is the Partial to label. // void Channelizer::channelize( Partial & partial ) const { using std::pow; // debugger << "channelizing Partial with " << partial.numBreakpoints() << " Breakpoints" << endl; // compute an amplitude-weighted average channel // label for each Partial: //double ampsum = 0.; double weightedlabel = 0.; Partial::const_iterator bp; for ( bp = partial.begin(); bp != partial.end(); ++bp ) { double f = bp.breakpoint().frequency(); double t = bp.time(); double weight = 1; if ( 0 != _ampWeighting ) { // This used to be an amplitude-weighted avg, but for many sounds, // particularly those for which the weighted avg would be very // different from the simple avg, the amplitude-weighted avg // emphasized the part of the sound in which the frequency estimates // are least reliable (e.g. a piano tone). The unweighted // average should give more intuitive results in most cases. // use sinusoidal amplitude: double a = bp.breakpoint().amplitude() * std::sqrt( 1. - bp.breakpoint().bandwidth() ); weight = pow( a, _ampWeighting ); } weightedlabel += weight * computeFractionalChannelNumber( t, f ); } int label = 0; if ( 0 < partial.numBreakpoints() ) // should always be the case { label = (int)((weightedlabel / partial.numBreakpoints()) + 0.5); } Assert( label >= 0 ); // assign label, and remember it, but // only if it is a valid (positive) // distillation label: partial.setLabel( label ); }
// --------------------------------------------------------------------------- // channelize (one Partial) // --------------------------------------------------------------------------- //! Label a Partial with the number of the frequency channel corresponding to //! the average frequency over all the Partial's Breakpoints. //! //! \param partial is the Partial to label. // void Channelizer::channelize( Partial & partial ) const { debugger << "channelizing Partial with " << partial.numBreakpoints() << " Breakpoints" << endl; // compute an amplitude-weighted average channel // label for each Partial: double ampsum = 0.; double weightedlabel = 0.; Partial::const_iterator bp; for ( bp = partial.begin(); bp != partial.end(); ++bp ) { // use sinusoidal amplitude: double a = bp.breakpoint().amplitude() * std::sqrt( 1. - bp.breakpoint().bandwidth() ); // This used to be an amplitude-weighted avg, but for many sounds, // particularly those for which the weighted avg would be very // different from the simple avg, the amplitude-weighted avg // emphasized the part of the sound in which the frequency estimates // are least reliable (e.g. a piano tone). The unweighted // average should give more intuitive results in most cases. double f = bp.breakpoint().frequency(); double t = bp.time(); double refFreq = _refChannelFreq->valueAt( t ) / _refChannelLabel; // weightedlabel += a * (f / refFreq); weightedlabel += a * giveMeN( f, refFreq, _stretchFactor ); ampsum += a; } int label; if ( ampsum > 0. ) // if ( 0 < partial.numBreakpoints() ) { label = (int)((weightedlabel / ampsum) + 0.5); } else // this should never happen, but just in case: { label = 0; } Assert( label >= 0 ); // assign label, and remember it, but // only if it is a valid (positive) // distillation label: partial.setLabel( label ); }
Partial *PartialManager::allocPartial(int partNum) { Partial *outPartial = NULL; // Get the first inactive partial for (unsigned int partialNum = 0; partialNum < synth->getPartialCount(); partialNum++) { if (!partialTable[partialNum]->isActive()) { outPartial = partialTable[partialNum]; break; } } if (outPartial != NULL) { outPartial->activate(partNum); } return outPartial; }
Partial *PartialManager::allocPartial(int partNum) { Partial *outPartial = NULL; // Get the first inactive partial for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) { if (!partialTable[partialNum]->isActive()) { outPartial = partialTable[partialNum]; break; } } if (outPartial != NULL) { outPartial->activate(partNum); } return outPartial; }
void Poly::terminate() { if (state == POLY_Inactive) { return; } for (int t = 0; t < 4; t++) { Partial *partial = partials[t]; if (partial != NULL) { partial->deactivate(); } } if (state != POLY_Inactive) { // FIXME: Throw out lots of debug output - this should never happen // (Deactivating the partials above should've made them each call partialDeactivated(), ultimately changing the state to POLY_Inactive) state = POLY_Inactive; } }
bool PartialBuilder::better_match( const Partial & part, const SpectralPeak & pk1, const SpectralPeak & pk2 ) { Assert( part.numBreakpoints() > 0 ); return freq_distance( part, pk1 ) < freq_distance( part, pk2 ); }
// --------------------------------------------------------------------------- // avgFrequency // --------------------------------------------------------------------------- //! Return the average frequency over all Breakpoints in this Partial. //! Return zero if the Partial has no Breakpoints. //! //! \param p is the Partial to evaluate //! \return the average frequency (Hz) of Breakpoints in the Partial p // double avgFrequency( const Partial & p ) { double avg = 0; for ( Partial::const_iterator it = p.begin(); it != p.end(); ++it ) { avg += it->frequency(); } if ( avg != 0 ) { avg /= p.numBreakpoints(); } return avg; }
Partial *PartialManager::allocPartial(int partNum) { Partial *outPartial = NULL; // Use the first inactive partial reserved for the specified part (if there are any) // Otherwise, use the last inactive partial, if any for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { if (!partialTable[i]->isActive()) { outPartial = partialTable[i]; if (partialReserveTable[i] == partNum) break; } } if (outPartial != NULL) { outPartial->activate(partNum); outPartial->age = 0; } return outPartial; }
// --------------------------------------------------------------------------- // findContribution (STATIC) // --------------------------------------------------------------------------- // Find and return an iterator range delimiting the portion of pshort that // should be spliced into the distilled Partial plong. If any Breakpoint // falls in a zero-amplitude region of plong, then pshort should contribute, // AND its onset should be retained (!!! This is the weird part!!!). // Therefore, if cbeg is not equal to cend, then cbeg is pshort.begin(). // std::pair< Partial::iterator, Partial::iterator > findContribution( Partial & pshort, const Partial & plong, double fadeTime, double gapTime ) { // a Breakpoint can only fit in the gap if there's // enough time to fade out pshort, introduce a // space of length gapTime, and fade in the rest // of plong (don't need to worry about the fade // in, because we are checking that plong is zero // at cbeg.time() + clearance anyway, so the fade // in must occur after that, and already be part of // plong): // // WRONG if cbeg is before the start of plong. // Changed so that all Partials are faded in and // out before distilling, so now the clearance // need only be the gap time: double clearance = gapTime; // fadeTime + gapTime; Partial::iterator cbeg = pshort.begin(); while ( cbeg != pshort.end() && ( plong.amplitudeAt( cbeg.time() ) > 0 || plong.amplitudeAt( cbeg.time() + clearance ) > 0 ) ) { ++cbeg; } Partial::iterator cend = cbeg; // if a gap is found, find the end of the // range of Breakpoints that fit in that // gap: while ( cend != pshort.end() && plong.amplitudeAt( cend.time() ) == 0 && plong.amplitudeAt( cend.time() + clearance ) == 0 ) { ++cend; } // if a gap is found, and it is big enough for at // least one Breakpoint, then include the // onset of the Partial: if ( cbeg != pshort.end() ) { cbeg = pshort.begin(); } return std::make_pair( cbeg, cend ); }
// --------------------------------------------------------------------------- // fixPhaseBetween // //! Fix the phase travel between two times by adjusting the //! frequency and phase of Breakpoints between those two times. //! //! This algorithm assumes that there is nothing interesting about the //! phases of the intervening Breakpoints, and modifies their frequencies //! as little as possible to achieve the correct amount of phase travel //! such that the frequencies and phases at the specified times //! match the stored values. The phases of all the Breakpoints between //! the specified times are recomputed. //! //! THIS DOES NOT YET TREAT NULL BREAKPOINTS DIFFERENTLY FROM OTHERS. //! //! \pre There must be at least one Breakpoint in the //! Partial between the specified times tbeg and tend. //! \post The phases and frequencies of the Breakpoints in the //! range have been recomputed such that an oscillator //! initialized to the parameters of the first Breakpoint //! will arrive at the parameters of the last Breakpoint, //! and all the intervening Breakpoints will be matched. //! \param p The partial whose phases and frequencies will be recomputed. //! The Breakpoint at this position is unaltered. //! \param tbeg The phases and frequencies of Breakpoints later than the //! one nearest this time will be modified. //! \param tend The phases and frequencies of Breakpoints earlier than the //! one nearest this time will be modified. Should be greater //! than tbeg, or else they will be swapped. // void fixPhaseBetween( Partial & p, double tbeg, double tend ) { if ( tbeg > tend ) { std::swap( tbeg, tend ); } // for Partials that do not extend over the entire // specified time range, just recompute phases from // beginning or end of the range: if ( p.endTime() < tend ) { // OK if start time is also after tbeg, will // just recompute phases from start of p. fixPhaseAfter( p, tbeg ); } else if ( p.startTime() > tbeg ) { fixPhaseBefore( p, tend ); } else { // invariant: // p begins before tbeg and ends after tend. Partial::iterator b = p.findNearest( tbeg ); Partial::iterator e = p.findNearest( tend ); // if there is a null Breakpoint n between b and e, then // should fix forward from b to n, and backward from // e to n. Otherwise, do this complicated thing. Partial::iterator nullbp = std::find_if( b, e, BreakpointUtils::isNull ); if ( nullbp != e ) { fixPhaseForward( b, nullbp ); fixPhaseBackward( nullbp, e ); } else { fixPhaseBetween( b, e ); } } }
// --------------------------------------------------------------------------- // absorb // --------------------------------------------------------------------------- //! Absorb another Partial's energy as noise (bandwidth), //! by accumulating the other's energy as noise energy //! in the portion of this Partial's envelope that overlaps //! (in time) with the other Partial's envelope. // void Partial::absorb( const Partial & other ) { Partial::iterator it = findAfter( other.startTime() ); while ( it != end() && !(it.time() > other.endTime()) ) { // only non-null (non-zero-amplitude) Breakpoints // abosrb noise energy because null Breakpoints // are used especially to reset the Partial phase, // and are not part of the normal analyasis data: if ( it->amplitude() > 0 ) { // absorb energy from other at the time // of this Breakpoint: double a = other.amplitudeAt( it.time() ); it->addNoiseEnergy( a * a ); } ++it; } }
// --------------------------------------------------------------------------- // distillSorter (static helper) // --------------------------------------------------------------------------- // Helper for sorting Partials for distilling. // static bool distillSorter( const Partial & lhs, const Partial & rhs ) { double ldur = lhs.duration(), rdur = rhs.duration(); if ( ldur != rdur ) { return ldur > rdur; } else { // What to do for same-duration Partials? // Need to do something consistent, should look // for energy? return lhs.startTime() < rhs.startTime(); /* double lpeak = PartialUtils::peakAmp( lhs ); double rpeak = PartialUtils::peakAmp( rhs ); return lhs > rhs; */ } }
// --------------------------------------------------------------------------- // timeOfPeakEnergy (static helper function) // --------------------------------------------------------------------------- // Return the time at which the given Partial attains its // maximum sinusoidal energy. // static double timeOfPeakEnergy( const Partial & p ) { Partial::const_iterator partialIter = p.begin(); double maxAmp = partialIter->amplitude() * std::sqrt( 1. - partialIter->bandwidth() ); double time = partialIter.time(); for ( ++partialIter; partialIter != p.end(); ++partialIter ) { double a = partialIter->amplitude() * std::sqrt( 1. - partialIter->bandwidth() ); if ( a > maxAmp ) { maxAmp = a; time = partialIter.time(); } } return time; }
// --------------------------------------------------------------------------- // NoiseRatioScaler function call operator // --------------------------------------------------------------------------- // Scale the relative noise content of the specified Partial according // to an envelope representing a (time-varying) noise energy // scale value. // void NoiseRatioScaler::operator()( Partial & p ) const { for ( Partial::iterator pos = p.begin(); pos != p.end(); ++pos ) { // compute new bandwidth value: double bw = pos.breakpoint().bandwidth(); if ( bw < 1. ) { double ratio = bw / (1. - bw); ratio *= env->valueAt( pos.time() ); bw = ratio / ( 1. + ratio ); } else { bw = 1.; } pos.breakpoint().setBandwidth( bw ); } }
std::vector<typename Partial::SolutionT> generate(Partial const & part, Constraint constr) { using SolutionVec = std::vector<typename Partial::SolutionT>; if (part.isFinished(constr)) { SolutionVec result{ part.getSolution() }; return result; } else { Stream<Partial> partList = part.refine(constr); SolutionVec result; forEach(std::move(partList), [&](Partial const & part){ SolutionVec lst = generate(part, constr); std::copy(lst.begin(), lst.end(), std::back_inserter(result)); }); return result; } }
// --------------------------------------------------------------------------- // weightedAvgFrequency // --------------------------------------------------------------------------- //! Return the average frequency over all Breakpoints in this Partial, //! weighted by the Breakpoint amplitudes. //! Return zero if the Partial has no Breakpoints. //! //! \param p is the Partial to evaluate //! \return the average frequency (Hz) of Breakpoints in the Partial p // double weightedAvgFrequency( const Partial & p ) { double avg = 0; double ampsum = 0; for ( Partial::const_iterator it = p.begin(); it != p.end(); ++it ) { avg += it->amplitude() * it->frequency(); ampsum += it->amplitude(); } if ( avg != 0 && ampsum != 0 ) { avg /= ampsum; } else { avg = 0; } return avg; }