5/18/2024 note: first three graphs are not updated as per last html, css and main.js
Main Components:
Flow:
Key Functions:
graph TD
A[Start] --> B[Initialize Variables and Settings]
B --> C[Setup Tonnetz Grid]
C --> D{User Interaction}
D -->|Touch bottomRight| E{Stream Active?}
E -->|No| F[Initialize Audio]
F --> G[Start Camera Stream]
G --> H[Start processFrame Interval]
E -->|Yes| I[Stop Stream and Clear Interval]
D -->|Touch topLeft| J[Cycle Update Interval]
D -->|Touch topRight| K[Cycle Note Mode]
D -->|Touch bottomLeft| L[Cycle Max Amplitude]
D -->|Double-click topLeft| M[Toggle Debug Overlay]
H --> N[processFrame]
N --> O[Capture Video Frame]
O --> P[Convert to Grayscale]
P --> Q[playAudio]
Q --> R[Split Frame: Left/Right]
R --> S[mapFrameToTonnetz]
S -->|Left Frame| T[Detect Motion, Map to Notes]
S -->|Right Frame| U[Detect Motion, Map to Notes]
T --> V[Update Oscillators]
U --> V
V --> W[Update Performance Metrics]
W -->|Debug Visible| X[Update Debug Text]
W --> Y{Continue Loop}
Y -->|Interval Active| N
I --> D
J --> D
K --> D
L --> D
M --> D
The mapFrameToTonnetz function:
frameData
as grayscale pixel data among width and height. prevFrameData
for previous frame for motion detection, panValue
for stereo panning -1 for left, 1 for right.gridWidth
, gridHeight
by dividing the frame into a 16x16 grid.newFrameData
array to store the current frame.frameData
with the previous frame prevFrameData
:
movingRegions
.movingRegions
by delta (motion strength) in descending order.tonnetzGrid
using gridX and gridY.newFrameData
.graph TD
A[Start: mapFrameToTonnetz] --> B[Receive input parameters]
B --> C[Calculate grid width]
C --> D[Calculate grid height]
D --> E[Create newFrameData array]
E --> F{Previous frame data exists?}
F -->|Yes| G[Initialize movingRegions array]
G --> H[Start loop over height]
H --> I[Start loop over width]
I --> J[Calculate pixel index]
J --> K[Calculate pixel difference]
K --> L{Pixel difference > 30?}
L -->|Yes| M[Calculate grid X coordinate]
M --> N[Calculate grid Y coordinate]
N --> O[Add region to movingRegions]
O --> P{Next width iteration?}
P -->|Yes| I
P -->|No| Q{Next height iteration?}
Q -->|Yes| H
Q -->|No| R[Sort movingRegions by strength]
F -->|No| R
R --> S[Initialize notes array]
S --> T[Start loop over top 4 regions]
T --> U[Get region data]
U --> V[Get frequency from tonnetzGrid]
V --> W[Calculate note amplitude]
W --> X{Note mode setting?}
X -->|major| Y[Set major chord harmonics]
X -->|minor| Z[Set minor chord harmonics]
X -->|dissonant| AA[Set dissonant chord harmonics]
Y --> AB[Add note to notes]
Z --> AB
AA --> AB
AB --> AC{Next region iteration?}
AC -->|Yes| T
AC -->|No| AD[Return notes and newFrameData]
AD --> AE[End]
The oscillator update logic in playAudio:
graph TD
A[Start Oscillator Updates] --> B[Set oscIndex to 0]
B --> C[Loop through oscillators]
C --> D{oscIndex less than allNotes length?}
D -->|Yes| E[Retrieve note data]
E --> F[Extract note properties]
F --> G[Determine oscillator type]
G --> H{frequency less than 400?}
H -->|Yes| I[Set type to square]
H -->|No| J{frequency less than 1000?}
J -->|Yes| K[Set type to triangle]
J -->|No| L[Set type to sine]
I --> M[Set frequency target]
K --> M
L --> M
M --> N[Set gain target]
N --> O[Set panner target]
O --> P[Mark oscillator active]
P --> Q{harmonics exist and enough oscillators?}
Q -->|Yes| R[Loop through harmonics]
R --> S[Get harmonic frequency]
S --> T[Get next oscillator]
T --> U[Set harmonic frequency target]
U --> V[Set harmonic gain target]
V --> W[Set harmonic panner target]
W --> X[Mark harmonic oscillator active]
X --> Y{Next harmonic?}
Y -->|Yes| R
Y -->|No| Z[Increase oscIndex by harmonic count]
Q -->|No| AA[Increase oscIndex]
Z --> AA
D -->|No| BB[Set gain target to 0]
BB --> CC[Mark oscillator inactive]
CC --> DD{Next oscillator?}
AA --> DD
DD -->|Yes| C
DD -->|No| EE[End Oscillator Updates]
Original:
playAudio
, starting around line 152.allNotes
is created by combining leftResult.notes
and rightResult.notes
.Updated:
playAudio
, starting around line 152.
Key differences:allNotes
is now sorted by amplitude in descending order: const allNotes = [...leftResult.notes, ...rightResult.notes].sort((a, b) => b.amplitude - a.amplitude);
.graph TD
A[Start Oscillator Updates] --> B[Set oscIndex to 0]
B --> C[Combine left and right notes]
C --> D[Sort notes by amplitude descending]
D --> E[Loop through oscillators]
E --> F{oscIndex less than allNotes length?}
F -->|Yes| G[Retrieve note data]
G --> H[Extract note properties]
H --> I[Set type to sine]
I --> J[Set frequency target]
J --> K[Set gain target]
K --> L[Set panner target]
L --> M[Mark oscillator active]
M --> N{harmonics exist and enough oscillators?}
N -->|Yes| O[Loop through harmonics]
O --> P[Get harmonic frequency]
P --> Q[Get next oscillator]
Q --> R[Set harmonic type to sine]
R --> S[Set harmonic frequency target]
S --> T[Set harmonic gain target]
T --> U[Set harmonic panner target]
U --> V[Mark harmonic oscillator active]
V --> W{Next harmonic?}
W -->|Yes| O
W -->|No| X[Increase oscIndex by harmonic count]
N -->|No| Y[Increase oscIndex]
X --> Y
F -->|No| Z[Set gain target to 0]
Z --> AA[Mark oscillator inactive]
AA --> BB{Next oscillator?}
Y --> BB
BB -->|Yes| E
BB -->|No| CC[End Oscillator Updates]