File size: 7,603 Bytes
dbc1132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fa39ce
dbc1132
9e600a5
dbc1132
 
 
 
 
 
9e600a5
dbc1132
 
 
4fa39ce
dbc1132
b7e38a0
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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Granite Speech WebGPU</title>
    <link rel="stylesheet" href="style.css?v=3">
</head>
<body>
    <div class="container">
        <!-- Header -->
        <h1>Granite Speech WebGPU</h1>
        <h2>Speech recognition and translation directly in your browser</h2>

        <!-- Audio Input Card -->
        <div class="input-card">
            <div class="input-options">
                <button id="recordBtn" class="input-tile" disabled>
                    <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M12 18.75a6 6 0 006-6v-1.5m-6 7.5a6 6 0 01-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 01-3-3V4.5a3 3 0 116 0v8.25a3 3 0 01-3 3z" />
                    </svg>
                    <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="display: none;">
                        <rect x="6" y="6" width="12" height="12" rx="1" />
                    </svg>
                    <span>Record</span>
                </button>
                <div class="divider"></div>
                <label class="input-tile file-label">
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" />
                    </svg>
                    <span>Upload</span>
                    <input type="file" id="audioFile" accept="audio/*" hidden>
                </label>
            </div>
            <div class="progress-bar">
                <div class="progress-fill" id="progressFill"></div>
            </div>
        </div>

        <!-- Status -->
        <div class="status-section" id="statusSection">
            <span class="status-dot" id="statusDot"></span>
            <span id="statusText">Loading...</span>
        </div>

        <!-- Audio Player (hidden initially) -->
        <div class="audio-player" id="audioPreview" style="display: none;">
            <button class="play-btn" id="playBtn">
                <svg class="play-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M8 5v14l11-7z"/>
                </svg>
                <svg class="pause-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" style="display: none;">
                    <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
                </svg>
            </button>
            <div class="waveform-container">
                <canvas id="waveformCanvas"></canvas>
                <div class="waveform-progress" id="waveformProgress"></div>
            </div>
            <span class="audio-time" id="audioTime">0:00</span>
            <button class="clear-btn" id="clearBtn" title="Clear">
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
                </svg>
            </button>
            <audio id="audioPlayer" style="display: none;"></audio>
        </div>

        <!-- Transcribe Section -->
        <div class="transcribe-section" id="transcribeSection" style="display: none;">
            <div class="task-row">
                <label for="promptSelect">Task:</label>
                <select id="promptSelect">
                    <option value="transcribe">Transcribe</option>
                    <option value="translate_en">Translate to English</option>
                    <option value="translate_fr">Translate to French</option>
                    <option value="translate_de">Translate to German</option>
                    <option value="translate_es">Translate to Spanish</option>
                    <option value="translate_pt">Translate to Portuguese</option>
                    <option value="translate_ja">Translate to Japanese</option>
                </select>
            </div>
            <label class="checkbox-row">
                <input type="checkbox" id="punctuationCheckbox" checked>
                <span>Add punctuation (English only)</span>
            </label>
            <button id="transcribeBtn" class="transcribe-btn" disabled>
                Transcribe
            </button>
        </div>

        <!-- Model Loading Progress -->
        <div class="model-progress" id="progressSection" style="display: none;">
            <span id="progressText">Loading model...</span>
        </div>

        <!-- Transcript Output -->
        <div class="transcript-card" id="transcriptCard" style="display: none;">
            <div class="transcript-header">
                <span>Transcript</span>
                <div class="transcript-actions">
                    <button id="copyBtn" class="icon-btn" title="Copy">
                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9.75a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" />
                        </svg>
                    </button>
                    <button id="downloadBtn" class="icon-btn" title="Download">
                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
                        </svg>
                    </button>
                </div>
            </div>
            <div class="transcript-output" id="outputText"></div>
        </div>

        <!-- Footer -->
        <div class="footer">
            Made with
            <a href="https://huggingface.co/ibm-granite/granite-4.0-1b-speech" target="_blank">Granite 4.0 1B Speech</a>
            and
            <a href="https://huggingface.co/docs/transformers.js" target="_blank">Transformers.js</a>
            <br>
            <span class="privacy-note">Your audio and transcription never leave your device</span>
        </div>
        <div class="gpu-info" id="gpuInfo"></div>
    </div>

    <!-- ORT global is retained for VAD (vad.js) and punctuation (punctuator.js) which use WASM -->
    <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.all.min.js"></script>
    <script src="vad.js?v=1"></script>
    <script src="punctuator.js?v=3"></script>
    <script type="module" src="app.js?v=55"></script>
</body>
</html>