Chapter 22

Phrase Detection

Learning Objectives

  • Understand musical phrases
  • Learn phrase boundary detection
  • Analyze song structure
  • See how MixMind detects phrases

What are Musical Phrases?

A phrase is a musical sentenceโ€”a complete musical thought, typically 4, 8, 16, or 32 bars long.

Songs are built from phrases:

  • Intro: Opening section (8-16 bars)
  • Verse: Main storytelling section (16 bars)
  • Chorus: Catchy, repeated section (8 bars)
  • Bridge: Contrasting section (8 bars)
  • Outro: Ending section (8-16 bars)
๐Ÿ’ก DJ Mixing:

Good mixes align phrases. Start track 2's intro when track 1's verse begins. This creates smooth, musical transitions.

Detecting Phrase Boundaries

Phrase boundaries often occur where there are changes in the music:

  • Energy changes (drums drop out, new instruments enter)
  • Structural changes (verse to chorus)
  • Silence or pauses
  • Drum fills
// Detect phrase boundaries using energy changes
function detectPhrases(audioBuffer, beatPositions) {
    const energy = calculateEnergyEnvelope(audioBuffer);
    const boundaries = [];
    
    // Look for energy drops (common at phrase ends)
    const windowSize = 4; // 4 beats
    
    for (let i = windowSize; i < beatPositions.length - windowSize; i++) {
        const beforeBeat = beatPositions[i - windowSize];
        const afterBeat = beatPositions[i];
        
        const energyBefore = getAverage(energy, beforeBeat, beatPositions[i]);
        const energyAfter = getAverage(energy, beatPositions[i], afterBeat);
        
        // Significant energy drop
        if (energyAfter < energyBefore * 0.7) {
            boundaries.push({
                beat: i,
                time: beatPositions[i],
                strength: energyBefore - energyAfter
            });
        }
    }
    
    return boundaries;
}

Structural Analysis

Detect the overall structure (intro, verse, chorus, etc.) by analyzing patterns:

// Analyze song structure
function analyzeStructure(audioBuffer, phrases) {
    const sections = [];
    
    // Extract features for each phrase
    const features = phrases.map(phrase => ({
        energy: calculateEnergy(phrase),
        spectralCentroid: calculateCentroid(phrase),
        chroma: calculateChroma(phrase)
    }));
    
    // Cluster similar phrases
    const clusters = clusterPhrases(features);
    
    // Label sections
    // First cluster = verse (most common)
    // Highest energy cluster = chorus
    // First section = intro
    // Last section = outro
    
    return sections;
}

// Simple clustering
function clusterPhrases(features) {
    // Use similarity metrics to group phrases
    // Similar energy + timbre = same section
}

๐ŸŽต MixMind Connection

How MixMind Detects Phrases

// MixMind's phrase detection
class PhraseDetector {
    async analyze(audioBuffer, beats) {
        // Calculate multi-band energy
        const energy = {
            bass: this.calculateEnergy(audioBuffer, 20, 250),
            mid: this.calculateEnergy(audioBuffer, 250, 4000),
            high: this.calculateEnergy(audioBuffer, 4000, 20000),
            total: this.calculateEnergy(audioBuffer, 20, 20000)
        };
        
        // Detect phrase boundaries
        const boundaries = this.detectBoundaries(energy, beats);
        
        // Group into phrases (typically 8 or 16 beats)
        const phrases = this.groupIntoPhrases(boundaries, beats);
        
        // Analyze structure
        const structure = this.analyzeStructure(phrases);
        
        return {
            phrases: phrases,
            structure: structure, // intro, verse, chorus, etc.
            phraseLength: this.detectPhraseLength(phrases)
        };
    }
    
    // Suggest mix points
    suggestMixPoints(structure) {
        const suggestions = [];
        
        // Intro end = good entry point
        if (structure.intro) {
            suggestions.push({
                time: structure.intro.end,
                reason: "End of intro - good for bringing in next track"
            });
        }
        
        // Before chorus = high energy entry
        if (structure.verse) {
            suggestions.push({
                time: structure.verse[0].start,
                reason: "Start of verse - build up to chorus"
            });
        }
        
        return suggestions;
    }
}

// Used for:
// - Suggesting mix-in/out points
// - Phrase-aligned transitions
// - Structure visualization

Exercise 22.1: Practice

Goal: Apply what you've learned in this chapter

Try the concepts from this chapter in your browser's console or a simple HTML file. Experiment with the code examples and modify them to see what happens.

๐Ÿ“ Quick Reference:

Review the key concepts from this chapter and make sure you understand each one before moving to the next chapter.