8 minute read

Tracciamento Armonico in Delta-Engine: Analisi Completa

1. Introduzione al Tracciamento Armonico

Il tracciamento armonico è un componente fondamentale dell’Analizzatore in Delta-Engine, che monitora la distribuzione delle frequenze e le caratteristiche spettrali della composizione in tempo reale. Questo sistema opera su due diverse scale temporali:

  • Analisi veloce (10 Hz): Calcola lo stato armonico istantaneo della composizione
  • Analisi lenta (1 Hz): Memorizza e classifica questi stati nel tempo

Insieme, questi due livelli di analisi forniscono a Delta-Engine una “percezione armonica” completa che guida le decisioni compositive e le transizioni di stato.

2. Tracciamento Armonico nell’Analisi Veloce (10 Hz)

L’analisi veloce è responsabile del rilevamento diretto delle caratteristiche armoniche in tempo reale.

2.1. Inizializzazione e Reset

All’inizio di ogni ciclo di analisi, i contatori vengono azzerati:

; Reset dei contatori armonici
kOctIdx = 0
while kOctIdx < $OTTAVE do
    tabw 0, kOctIdx, gi_active_octaves
    kOctIdx += 1
od
        
kRegIdx = 0
while kRegIdx < $REGISTRI do
    tabw 0, kRegIdx, gi_active_registers
    kRegIdx += 1
od
        
kMatrixIdx = 0
while kMatrixIdx < ($OTTAVE * $REGISTRI) do
    tabw 0, kMatrixIdx, gi_octave_register_matrix
    kMatrixIdx += 1
od

Questo reset è essenziale per garantire che ogni analisi rifletta solo gli eventi attualmente attivi, senza influenze dalle analisi precedenti.

2.2. Identificazione delle Caratteristiche Armoniche degli Eventi

Per ogni evento attivo, l’Analizzatore estrae l’informazione armonica:

; Per ogni evento attivo
if kAttackTime <= kCurrentTime && kAttackTime + kDuration >= kCurrentTime then
    kActiveEventsCount += 1
    
    ; Raccogli il valore ritmico per il calcolo del movimento spaziale
    kRhythm tab kEventIdx, gi_eve_hr
    kSumInverseRhythms += (1/kRhythm)

    ; Identifica ottava e registro dell'evento attivo
    kBehaviorId tab kEventIdx, gi_eve_comportamento
    if kBehaviorId >= 0 && kBehaviorId < gi_NUMComportamenti then
        kOctave tab kBehaviorId, gi_comp_OTTAVA
        kRegister tab kBehaviorId, gi_comp_REGISTRO
        
        ; Incrementa i contatori
        if kOctave >= 0 && kOctave < $OTTAVE then
            kOctCount tab kOctave, gi_active_octaves
            tabw kOctCount + 1, kOctave, gi_active_octaves
        endif
        
        if kRegister >= 0 && kRegister < $REGISTRI then
            kRegCount tab kRegister, gi_active_registers
            tabw kRegCount + 1, kRegister, gi_active_registers
        endif
        
        ; Incrementa la matrice ottava/registro
        kMatrixIdx = kOctave * $REGISTRI + kRegister
        if kMatrixIdx >= 0 && kMatrixIdx < ($OTTAVE * $REGISTRI) then
            kMatrixVal tab kMatrixIdx, gi_octave_register_matrix
            tabw kMatrixVal + 1, kMatrixIdx, gi_octave_register_matrix
            ; Aggiorna anche la matrice cumulativa
            kCumulativeVal tab kMatrixIdx, gi_cumulative_octave_register_matrix
            tabw kCumulativeVal + 1, kMatrixIdx, gi_cumulative_octave_register_matrix
        endif
    endif
endif

Logica armonica implementata:

  1. Ogni evento attivo ha un’ottava (0-9) e un registro (0-9) che definiscono la sua posizione nello spazio armonico
  2. L’ottava rappresenta la banda di frequenza principale (scala più ampia)
  3. Il registro rappresenta variazioni all’interno di un’ottava (scala più fine)
  4. La matrice ottava/registro (10×10) rappresenta una “mappa termica” della distribuzione armonica

2.3. Calcolo delle Metriche Armoniche Aggregate

Dopo aver analizzato tutti gli eventi, vengono calcolate metriche aggregate:

; Calcola metriche armoniche
kActiveOctaves = 0
kActiveRegisters = 0
kWeightedOctaveSum = 0
kTotalOctaveEvents = 0

; Conta ottave attive e calcola centroide
kOctIdx = 0
while kOctIdx < $OTTAVE do
    kOctCount tab kOctIdx, gi_active_octaves
    if kOctCount > 0 then
        kActiveOctaves += 1
        kWeightedOctaveSum += kOctIdx * kOctCount
        kTotalOctaveEvents += kOctCount
    endif
    kOctIdx += 1
od

; Calcola metriche armoniche
if kTotalOctaveEvents > 0 then
    kHarmonicDensity = kActiveOctaves / $OTTAVE
    kOctaveSpread = 1 - (kActiveOctaves / $OTTAVE)
    kSpectralCentroid = kWeightedOctaveSum / kTotalOctaveEvents
else
    kHarmonicDensity = 0
    kOctaveSpread = 0
    kSpectralCentroid = 0
endif

; Calcola il movimento spaziale medio
kCurrentSpatialMovement = (kActiveEventsCount > 0) ? kSumInverseRhythms / kActiveEventsCount : 0

Le metriche calcolate includono:

  1. Densità armonica (kHarmonicDensity)
    • Frazione di ottave attive simultaneamente (0-1)
    • Valore alto = distribuzione ampia delle frequenze
    • Valore basso = concentrazione delle frequenze
  2. Dispersione ottave (kOctaveSpread)
    • Complemento della densità armonica (1 - densità)
    • Misura di quanto è “sparsa” la distribuzione delle ottave
  3. Centroide spettrale (kSpectralCentroid)
    • Media pesata delle ottave attive
    • Indica il “centro di gravità” armonico (ottave alte vs. basse)
  4. Movimento spaziale (kCurrentSpatialMovement)
    • Derivato dai valori di ritmo degli eventi attivi
    • Valori alti = maggiore movimento nello spazio sonoro

2.4. Aggiornamento delle Variabili Globali

Infine, i valori calcolati vengono memorizzati in variabili globali:

; Aggiorna variabili globali
gk_current_overlap = kActiveEventsCount
gk_current_harmonic_density = kHarmonicDensity
gk_current_octave_spread = kOctaveSpread
gk_current_spectral_centroid = kSpectralCentroid
gk_current_spatial_movement = kCurrentSpatialMovement  

Queste variabili globali sono accessibili a tutto il sistema, in particolare al controller di transizione che le utilizzerà per prendere decisioni sulla evoluzione compositiva.

3. Tracciamento Armonico nell’Analisi Lenta (1 Hz)

L’analisi lenta si occupa principalmente di memorizzare l’evoluzione delle caratteristiche armoniche nel tempo e di classificarle in stati discreti.

3.1. Memorizzazione dell’Evoluzione Armonica

kMemTrig metro 1/gi_memory_resolution
if kMemTrig == 1 then
    kCurrentTime timeinsts
    kMemIdx = int(kCurrentTime / gi_memory_resolution)
    
    if kMemIdx >= 0 && kMemIdx < gi_memory_size then
        ; Memorizza i dati armonici nella "memoria compositiva"
        tabw gk_current_harmonic_density, kMemIdx, gi_memory_harmonic_density
        tabw gk_current_octave_spread, kMemIdx, gi_memory_octave_spread
        tabw gk_current_spectral_centroid, kMemIdx, gi_memory_spectral_centroid
        tabw gk_current_spatial_movement, kMemIdx, gi_memory_spatial_movement  

Caratteristiche chiave:

  1. Riutilizza i valori già calcolati dall’analisi veloce (non ricalcola)
  2. Li memorizza in tabelle che rappresentano l’evoluzione temporale
  3. L’indice (kMemIdx) rappresenta la posizione nel tempo all’interno della composizione

3.2. Classificazione in Stati Armonici Discreti

Un aspetto fondamentale dell’analisi lenta è la classificazione dei valori continui in stati discreti:

; Determina lo stato corrente
iCurrentDensity = i(gk_current_overlap)
iCurrentRegister = i(gk_current_octave_spread) 
iCurrentMovement = i(gk_current_spatial_movement)

iDensityState, iRegisterState, iMovementState determineCurrentState iCurrentDensity, iCurrentRegister, iCurrentMovement

L’opcode determineCurrentState in determineState.udo utilizza tabelle di soglie per questa classificazione:

; Determine register state
iRegisterIdx = 0
while iRegisterIdx < ftlen(gi_register_thresholds)-1 do
    iLowerBound tab_i iRegisterIdx, gi_register_thresholds
    iUpperBound tab_i iRegisterIdx+1, gi_register_thresholds
    
    if iRegisterSpread >= iLowerBound && iRegisterSpread < iUpperBound then
        iRegisterState = iRegisterIdx
        igoto register_done
    endif
    
    iRegisterIdx += 1
od

Le soglie sono definite come:

gi_register_thresholds ftgen 0, 0, 4, -2, 0, 0.3, 0.7, 1.001 ; Low, Mid, High

Questo significa che il valore di dispersione ottave (octave_spread) viene classificato in:

  • Stato 0 (Low): octave_spread < 0.3 (distribuzione concentrata)
  • Stato 1 (Mid): octave_spread tra 0.3 e 0.7 (distribuzione media)
  • Stato 2 (High): octave_spread > 0.7 (distribuzione molto sparsa)

3.3. Memorizzazione degli Stati Armonici

L’analisi lenta mantiene due tipi di memoria per gli stati:

; Aggiorna la cronologia degli stati usando il buffer circolare
kNextIndex = (gk_state_history_index + 1) % gi_state_history_size

; Memorizza il nuovo stato nella posizione corrente del buffer
tabw iRegisterState, kNextIndex, gi_state_history_register

; Aggiorna l'indice globale del buffer
gk_state_history_index = kNextIndex

; Memorizza lo stato nella cronologia temporale completa
tabw iRegisterState, kMemIdx, gi_memory_state_register
  1. Buffer circolare (gi_state_history_register)
    • Mantiene solo gli ultimi gi_state_history_size (tipicamente 10) stati
    • Permette analisi delle tendenze recenti
  2. Cronologia completa (gi_memory_state_register)
    • Traccia l’evoluzione degli stati per l’intera composizione
    • Supporta l’analisi e la visualizzazione a lungo termine

4. Interazione tra Analisi Veloce e Lenta

I due livelli di analisi armonica interagiscono in modi complementari:

┌───────────────┐
│ Analisi Veloce │◄─────┐
└───────────────┘      │
        │              │
        ▼              │
┌───────────────┐      │
│Variabili Globali│     │
└───────────────┘      │
        │              │
        ▼              │
┌───────────────┐      │
│ Analisi Lenta  │──────┘
└───────────────┘
        │
        ▼
┌───────────────┐
│  Memoria e     │
│Classificazione │
└───────────────┘
  • L’analisi veloce calcola lo stato armonico istantaneo e lo memorizza in variabili globali
  • L’analisi lenta riutilizza questi valori per:
    • Memorizzarli nella cronologia compositiva
    • Classificarli in stati discreti
    • Registrare l’evoluzione degli stati nel tempo

5. Utilizzo del Tracciamento Armonico per Decisioni Compositive

5.1. Per Analisi Compositiva Retrospettiva

; In calcDurationFactor.udo
opcode analyzeHarmonicMemory, iiii, ii
    iStartTime, iEndTime xin
    
    ; Calcola indici nella tabella di memoria
    iStartIdx = int(iStartTime / gi_memory_resolution)
    iEndIdx = int(iEndTime / gi_memory_resolution)
    
    ; Inizializza contatori
    iSumHarmonicDensity = 0
    iSumOctaveSpread = 0
    iMaxHarmonicDensity = 0
    iCount = 0
    
    ; Analizza il range temporale
    iIdx = iStartIdx
    while iIdx <= iEndIdx do
        iHarmonicDensity table iIdx, gi_memory_harmonic_density
        iOctaveSpread table iIdx, gi_memory_octave_spread
        
        iSumHarmonicDensity += iHarmonicDensity
        iSumOctaveSpread += iOctaveSpread
        iMaxHarmonicDensity = max(iMaxHarmonicDensity, iHarmonicDensity)
        
        iCount += 1
        iIdx += 1
    od
    
    ; Calcola valori aggregati
    iAvgHarmonicDensity = (iCount > 0) ? iSumHarmonicDensity / iCount : 0
    iAvgOctaveSpread = (iCount > 0) ? iSumOctaveSpread / iCount : 0
    
    xout iAvgHarmonicDensity, iMaxHarmonicDensity, iAvgOctaveSpread, iCount
endop

Questo opcode analizza un periodo temporale specifico per determinare caratteristiche armoniche aggregate, utili per adattare nuovi eventi al contesto.

5.2. Per Decisioni di Transizione di Stato

; In transControllerUtils.udo
opcode recordCurrentState, 0, 0
    ; Read current state from global variables populated by Analizzatore
    iCurDensity = i(gk_current_overlap)
    iCurRegister = i(gk_current_octave_spread)
    iCurMovement = i(gk_current_spatial_movement)
    
    ; Determine the current state using the existing determineCurrentState opcode
    iDensityState, iRegisterState, iMovementState determineCurrentState iCurDensity, iCurRegister, iCurMovement
    
    ; Update controller's knowledge of current state
    gi_tc_current_density = iDensityState
    gi_tc_current_register = iRegisterState
    gi_tc_current_movement = iMovementState
endop

Il TransitionController utilizza questi stati per determinare le future transizioni compositive.

5.3. Per la Generazione di Nuovi Comportamenti

; In TransitionController.orc (tramite BehaviorGenerator)
iInterpolatedRegister interpolateParameter gi_tc_source_register, gi_tc_target_register, iProgress, 0

; Ottava e registro mappati in base al parametro di stato
iMinOctave, iMaxOctave mapStateToParameter iInterpolatedRegister, "register"

Il sistema di transizione utilizza i parametri di stato armonico per guidare la generazione di nuovi comportamenti durante le transizioni.

6. Logica del Sistema Armonico in Delta-Engine

La logica del tracciamento armonico in Delta-Engine si basa su alcuni principi fondamentali:

6.1. Rappresentazione Multi-dimensionale dello Spazio Armonico

  • Dimensione verticale (ottave): Rappresenta la distribuzione delle frequenze in bande ampie
  • Dimensione orizzontale (registri): Rappresenta variazioni più fini all’interno di ciascuna ottava
  • Matrice bi-dimensionale: Fornisce una “mappa termica” completa della distribuzione armonica

6.2. Metriche Complementari

  • Densità armonica: Misura quante ottave sono attive simultaneamente
  • Dispersione ottave: Misura quanto è sparsa la distribuzione (spesso usata per decisioni di stato)
  • Centroide spettrale: Indica il centro di gravità armonico (grave vs acuto)
  • Movimento spaziale: Correlato alle caratteristiche ritmiche e alla loro percezione spaziale

6.3. Sistema di Classificazione a Stati Discreti

Semplifica lo spazio continuo delle caratteristiche armoniche in un sistema a stati discreti:

  • 3 stati di densità × 3 stati di registro × 3 stati di movimento = 27 stati possibili
  • Questa discretizzazione facilita le decisioni di transizione e la modellazione dell’evoluzione compositiva

6.4. Memoria a Due Livelli

  • Memoria recente (buffer circolari): Per decisioni a breve termine e analisi di tendenze immediate
  • Memoria storica (array completi): Per analisi a lungo termine e coerenza compositiva globale

7. Conclusione

Il tracciamento armonico in Delta-Engine rappresenta un sofisticato sistema di “percezione musicale” che combina:

  1. Analisi istantanea attraverso il monitoraggio ad alta frequenza (10 Hz)
  2. Memoria temporale attraverso la memorizzazione a bassa frequenza (1 Hz)
  3. Classificazione discretizzata attraverso il sistema di stati
  4. Informazione armonica multi-dimensionale attraverso ottave, registri e loro interazioni

Questa architettura consente a Delta-Engine di “ascoltare” la propria composizione in sviluppo, analizzarne le caratteristiche armoniche e prendere decisioni informate sulla sua evoluzione, creando una struttura musicale che bilancia coerenza e varietà nello spazio delle frequenze.