Beispiel #1
0
// ----------------------------------------------------------------------------
//
void BeatDetector::addFrequencyEvent( CEvent* eventHandler, unsigned freq_low, unsigned freq_high )
{
    unsigned samples_per_bin = m_sample_size / m_frequency_bins; // Frequency samples per bin

    unsigned bin_start = ((freq_low * m_sample_size) / m_samples_per_second) / samples_per_bin;
    unsigned bin_end =  ((freq_high * m_sample_size) / m_samples_per_second) / samples_per_bin;

    DWORD mask[BEAT_DETECTOR_MASK_SIZE];
    memset( mask, 0, sizeof(mask) );

    for ( unsigned bin=bin_start; bin <= bin_end && bin < m_frequency_bins; bin++ ) {
        mask[bin/32] |= (1L << (bin%32));
    }

    if ( studio.isDebug() ) {
        printf( "DEBUG: Beat detect %u-%uHz bins: %u to %u mask=", freq_low, freq_high, bin_start, bin_end );
        for ( int i=BEAT_DETECTOR_MASK_SIZE; i-- > 0; ) {
            printf( "%08x", mask[i] );
            if ( i > 0 )
                printf( ":" );
        }
        printf( "\n" );

        // output( "Beat detect %u-%uHz bins: %u to %u mask=%08x-%08x\n", freq_low, freq_high, bin_start, bin_end, mask[1],mask[0] );
    }

    m_event_handlers.push_back( BeatEvent( eventHandler, mask ) );
}
// Find evenly-spaced notes
// @param firstBeat A note Event performed on the first beat.
// @param secondBeat A note Event performed on the second beat.
// @param s The segment to look in for further events.
// @return Beat-defining data as a BeatEventVector
// @author Tom Breton (Tehom)
SelectAddEvenNotesCommand::BeatEventVector
SelectAddEvenNotesCommand::findBeatEvents(Segment &s,
                                   Event *firstBeat,
                                   Event *secondBeat)
{
    // Fixed parameters
    const float marginTimeRatio = 0.77;
    const float minTimeRatio = marginTimeRatio;
    const float maxTimeRatio = 1 / minTimeRatio;

    // Storage for the results.  We add the first two beats
    // immediately.  Caller promises us that they make sense.
    BeatEventVector result;
    result.push_back(BeatEvent(firstBeat, 0));
    result.push_back(BeatEvent(secondBeat, 0));

    // State variables tracking the most recent beat we've found.
    timeT currentBeatTime = secondBeat->getAbsoluteTime();
    timeT prevKnownBeatDuration =
        currentBeatTime - firstBeat->getAbsoluteTime();
    if (prevKnownBeatDuration <= 0) { return result; }

    // State variables tracking the current noteless stretch.  It may
    // be empty.
    int numSkippedBeats = 0;
    timeT prevKnownBeatTime = currentBeatTime;

    /**
     * We assume the beat-defining notes are separated by roughly the
     * same time-interval.  We handle noteless stretches where we
     * expect beats by counting the number of beats we missed.  We
     * drop the noteless stretch after the last true beat we find
     * because it can't do anything useful.  Stretches that contain
     * just notes that are too far from rhythmic expectations are
     * treated as noteless stretches.
     **/
    
    // Find beat-defining notes in the segment
    while (true) {

        // The time we would expect a new beat if the rhythm and tempo
        // were exactly constant.
        timeT expectedNoteTime = currentBeatTime + prevKnownBeatDuration;

        // An acceptable interval for the next beat.
        timeT minNextDuration = float(prevKnownBeatDuration) * minTimeRatio;
        timeT maxNextDuration = float(prevKnownBeatDuration) * maxTimeRatio;
        timeT minNextNoteTime = currentBeatTime + minNextDuration;
        timeT maxNextNoteTime = currentBeatTime + maxNextDuration;

        Segment::const_iterator startRangeIter = s.findTime(minNextNoteTime);
        Segment::const_iterator endRangeIter = s.findTime(maxNextNoteTime);

       // Break if there won't be more notes to find.
       if (startRangeIter == s.end()) { break; }

       // Candidate variable.  NULL means nothing found.
       Event *nextBeat = 0;
       // Scoring variable, how much the best candidate note differs
       // from expectedNoteTime.  Smaller is better.
       timeT nearestMiss = std::numeric_limits<timeT>::max();
       
       for (Segment::const_iterator i = startRangeIter;
            i != endRangeIter;
            ++i) {
           Event *e = *i;
           // Only consider notes.
           if (e->isa(Note::EventType)) {
               const timeT missedBy =
                   std::abs(e->getAbsoluteTime() - expectedNoteTime);

               // Track the best candidate.
               if (missedBy < nearestMiss) {
                   nextBeat = e;
                   nearestMiss = missedBy;
               }
           }
       }

       
       if (nextBeat) {
           const timeT nextBeatTime = nextBeat->getAbsoluteTime();
           const timeT stretchDuration = nextBeatTime - prevKnownBeatTime;
           const int numIncludedBeats = numSkippedBeats + 1;
           // The absolute time of the beat immediately before
           // nextBeatTime, possibly calculated by BeatInterpolator
           timeT prevFoundBeatTime =
               (numSkippedBeats > 0) ?
               (prevKnownBeatTime +
                BeatInterpolator::
                getLastBeatRelativeTime(stretchDuration,
                                        prevKnownBeatDuration,
                                        numIncludedBeats)) :
               currentBeatTime;

           // Add this beat
           result.push_back(BeatEvent(nextBeat,
                                      numSkippedBeats,
                                      BeatInterpolator(stretchDuration,
                                                       prevKnownBeatDuration,
                                                       numIncludedBeats)));

           // Since we'll continue from a known beat, record that we
           // haven't skipped any beats.
           numSkippedBeats = 0;

           // Set variables for the next iteration of the loop.
           prevKnownBeatDuration = nextBeatTime - prevFoundBeatTime;
           currentBeatTime = nextBeatTime;
           prevKnownBeatTime = nextBeatTime;
       } else {
           // If we found no candidates, we began or are already in a
           // noteless stretch.  In either case we count one more
           // missed beat and step forward in time.
           ++numSkippedBeats;
           currentBeatTime = expectedNoteTime;
       }
    }

    return result;
}