File size: 5,676 Bytes
54f863b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import React from "react";
import { Download, FileDown, Activity, Info, TrendingUp, Key } from "lucide-react";
import { NoteEvent } from "../utils/midiWriter";

interface StatsPanelProps {
  notes: NoteEvent[];
  bpm: number;
  duration: number;
  selectedPreset: string;
  onDownloadMidi: () => void;
  isDownloading: boolean;
}

export default function StatsPanel({
  notes,
  bpm,
  duration,
  selectedPreset,
  onDownloadMidi,
  isDownloading
}: StatsPanelProps) {
  
  if (notes.length === 0) return null;

  // Calculate stats
  const noteCount = notes.length;
  const density = duration > 0 ? (noteCount / duration).toFixed(1) : "0.0";
  
  const pitches = notes.map(n => n.midi_note);
  const minPitch = Math.min(...pitches);
  const maxPitch = Math.max(...pitches);

  const getNoteName = (midiNum: number) => {
    const names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
    const octave = Math.floor(midiNum / 12) - 1;
    const noteName = names[midiNum % 12];
    return `${noteName}${octave}`;
  };

  // Estimate a simple musical Key based on Pitch frequency tally
  const estimateKey = () => {
    const pitchCounts = new Array(12).fill(0);
    pitches.forEach(p => {
      pitchCounts[p % 12]++;
    });

    const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
    let maxIdx = 0;
    let maxCount = 0;
    for (let i = 0; i < 12; i++) {
      if (pitchCounts[i] > maxCount) {
        maxCount = pitchCounts[i];
        maxIdx = i;
      }
    }

    // Return estimated tonic + dynamic modality heuristic
    // If notes contain G and E relative to C, say Major, otherwise Minor
    const tonic = keyNames[maxIdx];
    const isMajor = pitchCounts[(maxIdx + 4) % 12] >= pitchCounts[(maxIdx + 3) % 12];
    return `${tonic} ${isMajor ? "Major" : "Minor"}`;
  };

  return (
    <div className="bg-slate-900 border border-slate-800 rounded-xl p-5 shadow-xl space-y-6">
      
      {/* Title */}
      <div className="flex items-center gap-2 pb-3 border-b border-slate-800">
        <TrendingUp className="w-5 h-5 text-emerald-400" />
        <h3 className="text-sm font-semibold tracking-wider text-slate-200 uppercase font-sans">
          TRANSCRIPTION METRICS
        </h3>
      </div>

      {/* Grid of stats */}
      <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
        
        {/* Total notes */}
        <div className="bg-slate-950 border border-slate-800/60 rounded-xl p-4 text-center">
          <span className="text-[10px] text-zinc-500 font-mono block uppercase">Total Notes</span>
          <span className="text-2xl font-bold font-mono text-emerald-400 mt-1 block">
            {noteCount}
          </span>
        </div>

        {/* Note Density */}
        <div className="bg-slate-950 border border-slate-800/60 rounded-xl p-4 text-center">
          <span className="text-[10px] text-zinc-500 font-mono block uppercase">Density</span>
          <span className="text-2xl font-bold font-mono text-emerald-400 mt-1 block">
            {density} <span className="text-xs text-slate-500 font-normal">n/s</span>
          </span>
        </div>

        {/* Est. Key Signature */}
        <div className="bg-slate-950 border border-slate-800/60 rounded-xl p-4 text-center">
          <span className="text-[10px] text-zinc-500 font-mono block uppercase">Est. Key TONALITY</span>
          <span className="text-lg font-bold font-mono text-emerald-400 mt-2 block flex items-center justify-center gap-1">
            <Key className="w-3.5 h-3.5 text-amber-500 inline" />
            {estimateKey()}
          </span>
        </div>

        {/* Pitch Range */}
        <div className="bg-slate-950 border border-slate-800/60 rounded-xl p-4 text-center">
          <span className="text-[10px] text-zinc-500 font-mono block uppercase">Pitch Range</span>
          <span className="text-sm font-semibold font-mono text-emerald-400 mt-2.5 block truncate">
            {getNoteName(minPitch)} ~ {getNoteName(maxPitch)}
          </span>
        </div>

      </div>

      {/* Helper notice */}
      <div className="bg-slate-950/60 border border-slate-800 text-slate-400 p-4 rounded-xl flex gap-3 text-xs leading-relaxed">
        <Info className="w-5 h-5 text-emerald-400 flex-shrink-0 mt-0.5" />
        <div>
          <strong className="text-zinc-300 font-sans">DAW Ready MIDI Format</strong>
          <p className="font-sans text-slate-400 mt-0.5 select-none">
            Our exported file includes absolute note events, velocity mappings, and sustain meta events. It is saved in Format 0 standard MIDI structure compatibility, immediately readable in Ableton Live, Logic Pro, GarageBand, Pro Tools, or FL Studio templates.
          </p>
        </div>
      </div>

      {/* Downloads Action Panel */}
      <div className="pt-2">
        <button
          onClick={onDownloadMidi}
          disabled={isDownloading}
          className="w-full flex items-center justify-center gap-2 bg-emerald-500 hover:bg-emerald-600 font-sans text-slate-950 hover:text-slate-900 font-bold py-3.5 px-4 rounded-xl shadow-lg shadow-emerald-500/10 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200"
        >
          {isDownloading ? (
            <>
              <div className="w-4 h-4 border-2 border-slate-950 border-t-transparent rounded-full animate-spin" />
              <span>COMPILING MIDI TRACK...</span>
            </>
          ) : (
            <>
              <FileDown className="w-5 h-5" />
              <span>EXPORT MIDI FILE (.mid)</span>
            </>
          )}
        </button>
      </div>

    </div>
  );
}