// ---------------------------------------------------------------------------- // 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; }