Spaces:
Running
Running
whisper.swiftui : add model download list & bench methods (#2546)
Browse files* swift : fix resources & exclude build
* whisper : impl whisper_timings struct & api
* whisper.swiftui : model list & bench methods
* whisper : return ptr for whisper_get_timings
* revert unnecessary change
* whisper : avoid designated initializer
* whisper.swiftui: code style changes
* whisper.swiftui : get device name / os from UIDevice
* whisper.swiftui : fix UIDevice usage
* whisper.swiftui : add memcpy and ggml_mul_mat (commented)
- Package.swift +5 -4
- examples/whisper.swiftui/whisper.cpp.swift/LibWhisper.swift +83 -0
- examples/whisper.swiftui/whisper.swiftui.demo/Models/Model.swift +17 -0
- examples/whisper.swiftui/whisper.swiftui.demo/Models/WhisperState.swift +52 -11
- examples/whisper.swiftui/whisper.swiftui.demo/UI/ContentView.swift +115 -4
- examples/whisper.swiftui/whisper.swiftui.demo/UI/DownloadButton.swift +102 -0
- examples/whisper.swiftui/whisper.swiftui.xcodeproj/project.pbxproj +8 -0
- include/whisper.h +8 -0
- src/whisper.cpp +13 -0
Package.swift
CHANGED
|
@@ -18,16 +18,17 @@ let package = Package(
|
|
| 18 |
name: "whisper",
|
| 19 |
path: ".",
|
| 20 |
exclude: [
|
|
|
|
| 21 |
"bindings",
|
| 22 |
"cmake",
|
| 23 |
-
"coreml",
|
| 24 |
"examples",
|
| 25 |
-
"
|
| 26 |
"models",
|
| 27 |
"samples",
|
| 28 |
"tests",
|
| 29 |
"CMakeLists.txt",
|
| 30 |
-
"Makefile"
|
|
|
|
| 31 |
],
|
| 32 |
sources: [
|
| 33 |
"ggml/src/ggml.c",
|
|
@@ -38,7 +39,7 @@ let package = Package(
|
|
| 38 |
"ggml/src/ggml-quants.c",
|
| 39 |
"ggml/src/ggml-metal.m"
|
| 40 |
],
|
| 41 |
-
resources: [.process("ggml-metal.metal")],
|
| 42 |
publicHeadersPath: "spm-headers",
|
| 43 |
cSettings: [
|
| 44 |
.unsafeFlags(["-Wno-shorten-64-to-32", "-O3", "-DNDEBUG"]),
|
|
|
|
| 18 |
name: "whisper",
|
| 19 |
path: ".",
|
| 20 |
exclude: [
|
| 21 |
+
"build",
|
| 22 |
"bindings",
|
| 23 |
"cmake",
|
|
|
|
| 24 |
"examples",
|
| 25 |
+
"scripts",
|
| 26 |
"models",
|
| 27 |
"samples",
|
| 28 |
"tests",
|
| 29 |
"CMakeLists.txt",
|
| 30 |
+
"Makefile",
|
| 31 |
+
"ggml/src/ggml-metal-embed.metal"
|
| 32 |
],
|
| 33 |
sources: [
|
| 34 |
"ggml/src/ggml.c",
|
|
|
|
| 39 |
"ggml/src/ggml-quants.c",
|
| 40 |
"ggml/src/ggml-metal.m"
|
| 41 |
],
|
| 42 |
+
resources: [.process("ggml/src/ggml-metal.metal")],
|
| 43 |
publicHeadersPath: "spm-headers",
|
| 44 |
cSettings: [
|
| 45 |
.unsafeFlags(["-Wno-shorten-64-to-32", "-O3", "-DNDEBUG"]),
|
examples/whisper.swiftui/whisper.cpp.swift/LibWhisper.swift
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
import Foundation
|
|
|
|
| 2 |
import whisper
|
| 3 |
|
| 4 |
enum WhisperError: Error {
|
|
@@ -55,11 +56,93 @@ actor WhisperContext {
|
|
| 55 |
return transcription
|
| 56 |
}
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
static func createContext(path: String) throws -> WhisperContext {
|
| 59 |
var params = whisper_context_default_params()
|
| 60 |
#if targetEnvironment(simulator)
|
| 61 |
params.use_gpu = false
|
| 62 |
print("Running on the simulator, using CPU")
|
|
|
|
|
|
|
| 63 |
#endif
|
| 64 |
let context = whisper_init_from_file_with_params(path, params)
|
| 65 |
if let context {
|
|
|
|
| 1 |
import Foundation
|
| 2 |
+
import UIKit
|
| 3 |
import whisper
|
| 4 |
|
| 5 |
enum WhisperError: Error {
|
|
|
|
| 56 |
return transcription
|
| 57 |
}
|
| 58 |
|
| 59 |
+
static func benchMemcpy(nThreads: Int32) async -> String {
|
| 60 |
+
return String.init(cString: whisper_bench_memcpy_str(nThreads))
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
static func benchGgmlMulMat(nThreads: Int32) async -> String {
|
| 64 |
+
return String.init(cString: whisper_bench_ggml_mul_mat_str(nThreads))
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
private func systemInfo() -> String {
|
| 68 |
+
var info = ""
|
| 69 |
+
if (ggml_cpu_has_neon() != 0) { info += "NEON " }
|
| 70 |
+
if (ggml_cpu_has_metal() != 0) { info += "METAL " }
|
| 71 |
+
if (ggml_cpu_has_blas() != 0) { info += "BLAS " }
|
| 72 |
+
return String(info.dropLast())
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
func benchFull(modelName: String, nThreads: Int32) async -> String {
|
| 76 |
+
let nMels = whisper_model_n_mels(context)
|
| 77 |
+
if (whisper_set_mel(context, nil, 0, nMels) != 0) {
|
| 78 |
+
return "error: failed to set mel"
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
// heat encoder
|
| 82 |
+
if (whisper_encode(context, 0, nThreads) != 0) {
|
| 83 |
+
return "error: failed to encode"
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
var tokens = [whisper_token](repeating: 0, count: 512)
|
| 87 |
+
|
| 88 |
+
// prompt heat
|
| 89 |
+
if (whisper_decode(context, &tokens, 256, 0, nThreads) != 0) {
|
| 90 |
+
return "error: failed to decode"
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
// text-generation heat
|
| 94 |
+
if (whisper_decode(context, &tokens, 1, 256, nThreads) != 0) {
|
| 95 |
+
return "error: failed to decode"
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
whisper_reset_timings(context)
|
| 99 |
+
|
| 100 |
+
// actual run
|
| 101 |
+
if (whisper_encode(context, 0, nThreads) != 0) {
|
| 102 |
+
return "error: failed to encode"
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
// text-generation
|
| 106 |
+
for i in 0..<256 {
|
| 107 |
+
if (whisper_decode(context, &tokens, 1, Int32(i), nThreads) != 0) {
|
| 108 |
+
return "error: failed to decode"
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
// batched decoding
|
| 113 |
+
for _ in 0..<64 {
|
| 114 |
+
if (whisper_decode(context, &tokens, 5, 0, nThreads) != 0) {
|
| 115 |
+
return "error: failed to decode"
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
// prompt processing
|
| 120 |
+
for _ in 0..<16 {
|
| 121 |
+
if (whisper_decode(context, &tokens, 256, 0, nThreads) != 0) {
|
| 122 |
+
return "error: failed to decode"
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
whisper_print_timings(context)
|
| 127 |
+
|
| 128 |
+
let deviceModel = await UIDevice.current.model
|
| 129 |
+
let systemName = await UIDevice.current.systemName
|
| 130 |
+
let systemInfo = self.systemInfo()
|
| 131 |
+
let timings: whisper_timings = whisper_get_timings(context).pointee
|
| 132 |
+
let encodeMs = String(format: "%.2f", timings.encode_ms)
|
| 133 |
+
let decodeMs = String(format: "%.2f", timings.decode_ms)
|
| 134 |
+
let batchdMs = String(format: "%.2f", timings.batchd_ms)
|
| 135 |
+
let promptMs = String(format: "%.2f", timings.prompt_ms)
|
| 136 |
+
return "| \(deviceModel) | \(systemName) | \(systemInfo) | \(modelName) | \(nThreads) | 1 | \(encodeMs) | \(decodeMs) | \(batchdMs) | \(promptMs) | <todo> |"
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
static func createContext(path: String) throws -> WhisperContext {
|
| 140 |
var params = whisper_context_default_params()
|
| 141 |
#if targetEnvironment(simulator)
|
| 142 |
params.use_gpu = false
|
| 143 |
print("Running on the simulator, using CPU")
|
| 144 |
+
#else
|
| 145 |
+
params.flash_attn = true // Enabled by default for Metal
|
| 146 |
#endif
|
| 147 |
let context = whisper_init_from_file_with_params(path, params)
|
| 148 |
if let context {
|
examples/whisper.swiftui/whisper.swiftui.demo/Models/Model.swift
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Foundation
|
| 2 |
+
|
| 3 |
+
struct Model: Identifiable {
|
| 4 |
+
var id = UUID()
|
| 5 |
+
var name: String
|
| 6 |
+
var info: String
|
| 7 |
+
var url: String
|
| 8 |
+
|
| 9 |
+
var filename: String
|
| 10 |
+
var fileURL: URL {
|
| 11 |
+
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(filename)
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
func fileExists() -> Bool {
|
| 15 |
+
FileManager.default.fileExists(atPath: fileURL.path)
|
| 16 |
+
}
|
| 17 |
+
}
|
examples/whisper.swiftui/whisper.swiftui.demo/Models/WhisperState.swift
CHANGED
|
@@ -14,7 +14,7 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
|
| 14 |
private var recordedFile: URL? = nil
|
| 15 |
private var audioPlayer: AVAudioPlayer?
|
| 16 |
|
| 17 |
-
private var
|
| 18 |
Bundle.main.url(forResource: "ggml-base.en", withExtension: "bin", subdirectory: "models")
|
| 19 |
}
|
| 20 |
|
|
@@ -28,23 +28,59 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
|
| 28 |
|
| 29 |
override init() {
|
| 30 |
super.init()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
do {
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
canTranscribe = true
|
| 34 |
} catch {
|
| 35 |
print(error.localizedDescription)
|
| 36 |
-
messageLog += "\(error.localizedDescription)\n"
|
| 37 |
}
|
| 38 |
}
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
messageLog += "Loaded model \(modelUrl.lastPathComponent)\n"
|
| 45 |
-
} else {
|
| 46 |
-
messageLog += "Could not locate model\n"
|
| 47 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
}
|
| 49 |
|
| 50 |
func transcribeSample() async {
|
|
@@ -160,3 +196,8 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
|
| 160 |
isRecording = false
|
| 161 |
}
|
| 162 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
private var recordedFile: URL? = nil
|
| 15 |
private var audioPlayer: AVAudioPlayer?
|
| 16 |
|
| 17 |
+
private var builtInModelUrl: URL? {
|
| 18 |
Bundle.main.url(forResource: "ggml-base.en", withExtension: "bin", subdirectory: "models")
|
| 19 |
}
|
| 20 |
|
|
|
|
| 28 |
|
| 29 |
override init() {
|
| 30 |
super.init()
|
| 31 |
+
loadModel()
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
func loadModel(path: URL? = nil, log: Bool = true) {
|
| 35 |
do {
|
| 36 |
+
whisperContext = nil
|
| 37 |
+
if (log) { messageLog += "Loading model...\n" }
|
| 38 |
+
let modelUrl = path ?? builtInModelUrl
|
| 39 |
+
if let modelUrl {
|
| 40 |
+
whisperContext = try WhisperContext.createContext(path: modelUrl.path())
|
| 41 |
+
if (log) { messageLog += "Loaded model \(modelUrl.lastPathComponent)\n" }
|
| 42 |
+
} else {
|
| 43 |
+
if (log) { messageLog += "Could not locate model\n" }
|
| 44 |
+
}
|
| 45 |
canTranscribe = true
|
| 46 |
} catch {
|
| 47 |
print(error.localizedDescription)
|
| 48 |
+
if (log) { messageLog += "\(error.localizedDescription)\n" }
|
| 49 |
}
|
| 50 |
}
|
| 51 |
+
|
| 52 |
+
func benchCurrentModel() async {
|
| 53 |
+
if whisperContext == nil {
|
| 54 |
+
messageLog += "Cannot bench without loaded model\n"
|
| 55 |
+
return
|
|
|
|
|
|
|
|
|
|
| 56 |
}
|
| 57 |
+
messageLog += "Running benchmark for loaded model\n"
|
| 58 |
+
let result = await whisperContext?.benchFull(modelName: "<current>", nThreads: Int32(min(4, cpuCount())))
|
| 59 |
+
if (result != nil) { messageLog += result! + "\n" }
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
func bench(models: [Model]) async {
|
| 63 |
+
let nThreads = Int32(min(4, cpuCount()))
|
| 64 |
+
|
| 65 |
+
// messageLog += "Running memcpy benchmark\n"
|
| 66 |
+
// messageLog += await WhisperContext.benchMemcpy(nThreads: nThreads) + "\n"
|
| 67 |
+
//
|
| 68 |
+
// messageLog += "Running ggml_mul_mat benchmark with \(nThreads) threads\n"
|
| 69 |
+
// messageLog += await WhisperContext.benchGgmlMulMat(nThreads: nThreads) + "\n"
|
| 70 |
+
|
| 71 |
+
messageLog += "Running benchmark for all downloaded models\n"
|
| 72 |
+
messageLog += "| CPU | OS | Config | Model | Th | FA | Enc. | Dec. | Bch5 | PP | Commit |\n"
|
| 73 |
+
messageLog += "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n"
|
| 74 |
+
for model in models {
|
| 75 |
+
loadModel(path: model.fileURL, log: false)
|
| 76 |
+
if whisperContext == nil {
|
| 77 |
+
messageLog += "Cannot bench without loaded model\n"
|
| 78 |
+
break
|
| 79 |
+
}
|
| 80 |
+
let result = await whisperContext?.benchFull(modelName: model.name, nThreads: nThreads)
|
| 81 |
+
if (result != nil) { messageLog += result! + "\n" }
|
| 82 |
+
}
|
| 83 |
+
messageLog += "Benchmarking completed\n"
|
| 84 |
}
|
| 85 |
|
| 86 |
func transcribeSample() async {
|
|
|
|
| 196 |
isRecording = false
|
| 197 |
}
|
| 198 |
}
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
fileprivate func cpuCount() -> Int {
|
| 202 |
+
ProcessInfo.processInfo.processorCount
|
| 203 |
+
}
|
examples/whisper.swiftui/whisper.swiftui.demo/UI/ContentView.swift
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import SwiftUI
|
| 2 |
import AVFoundation
|
|
|
|
| 3 |
|
| 4 |
struct ContentView: View {
|
| 5 |
@StateObject var whisperState = WhisperState()
|
|
@@ -29,15 +30,125 @@ struct ContentView: View {
|
|
| 29 |
Text(verbatim: whisperState.messageLog)
|
| 30 |
.frame(maxWidth: .infinity, alignment: .leading)
|
| 31 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
}
|
| 33 |
.navigationTitle("Whisper SwiftUI Demo")
|
| 34 |
.padding()
|
| 35 |
}
|
| 36 |
}
|
| 37 |
-
}
|
| 38 |
|
| 39 |
-
struct
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
}
|
| 43 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import SwiftUI
|
| 2 |
import AVFoundation
|
| 3 |
+
import Foundation
|
| 4 |
|
| 5 |
struct ContentView: View {
|
| 6 |
@StateObject var whisperState = WhisperState()
|
|
|
|
| 30 |
Text(verbatim: whisperState.messageLog)
|
| 31 |
.frame(maxWidth: .infinity, alignment: .leading)
|
| 32 |
}
|
| 33 |
+
.font(.footnote)
|
| 34 |
+
.padding()
|
| 35 |
+
.background(Color.gray.opacity(0.1))
|
| 36 |
+
.cornerRadius(10)
|
| 37 |
+
|
| 38 |
+
HStack {
|
| 39 |
+
Button("Clear Logs", action: {
|
| 40 |
+
whisperState.messageLog = ""
|
| 41 |
+
})
|
| 42 |
+
.font(.footnote)
|
| 43 |
+
.buttonStyle(.bordered)
|
| 44 |
+
|
| 45 |
+
Button("Copy Logs", action: {
|
| 46 |
+
UIPasteboard.general.string = whisperState.messageLog
|
| 47 |
+
})
|
| 48 |
+
.font(.footnote)
|
| 49 |
+
.buttonStyle(.bordered)
|
| 50 |
+
|
| 51 |
+
Button("Bench", action: {
|
| 52 |
+
Task {
|
| 53 |
+
await whisperState.benchCurrentModel()
|
| 54 |
+
}
|
| 55 |
+
})
|
| 56 |
+
.font(.footnote)
|
| 57 |
+
.buttonStyle(.bordered)
|
| 58 |
+
.disabled(!whisperState.canTranscribe)
|
| 59 |
+
|
| 60 |
+
Button("Bench All", action: {
|
| 61 |
+
Task {
|
| 62 |
+
await whisperState.bench(models: ModelsView.getDownloadedModels())
|
| 63 |
+
}
|
| 64 |
+
})
|
| 65 |
+
.font(.footnote)
|
| 66 |
+
.buttonStyle(.bordered)
|
| 67 |
+
.disabled(!whisperState.canTranscribe)
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
NavigationLink(destination: ModelsView(whisperState: whisperState)) {
|
| 71 |
+
Text("View Models")
|
| 72 |
+
}
|
| 73 |
+
.font(.footnote)
|
| 74 |
+
.padding()
|
| 75 |
}
|
| 76 |
.navigationTitle("Whisper SwiftUI Demo")
|
| 77 |
.padding()
|
| 78 |
}
|
| 79 |
}
|
|
|
|
| 80 |
|
| 81 |
+
struct ModelsView: View {
|
| 82 |
+
@ObservedObject var whisperState: WhisperState
|
| 83 |
+
@Environment(\.dismiss) var dismiss
|
| 84 |
+
|
| 85 |
+
private static let models: [Model] = [
|
| 86 |
+
Model(name: "tiny", info: "(F16, 75 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin", filename: "tiny.bin"),
|
| 87 |
+
Model(name: "tiny-q5_1", info: "(31 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny-q5_1.bin", filename: "tiny-q5_1.bin"),
|
| 88 |
+
Model(name: "tiny-q8_0", info: "(42 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny-q8_0.bin", filename: "tiny-q8_0.bin"),
|
| 89 |
+
Model(name: "tiny.en", info: "(F16, 75 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en.bin", filename: "tiny.en.bin"),
|
| 90 |
+
Model(name: "tiny.en-q5_1", info: "(31 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en-q5_1.bin", filename: "tiny.en-q5_1.bin"),
|
| 91 |
+
Model(name: "tiny.en-q8_0", info: "(42 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en-q8_0.bin", filename: "tiny.en-q8_0.bin"),
|
| 92 |
+
Model(name: "base", info: "(F16, 142 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin", filename: "base.bin"),
|
| 93 |
+
Model(name: "base-q5_1", info: "(57 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base-q5_1.bin", filename: "base-q5_1.bin"),
|
| 94 |
+
Model(name: "base-q8_0", info: "(78 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base-q8_0.bin", filename: "base-q8_0.bin"),
|
| 95 |
+
Model(name: "base.en", info: "(F16, 142 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin", filename: "base.en.bin"),
|
| 96 |
+
Model(name: "base.en-q5_1", info: "(57 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en-q5_1.bin", filename: "base.en-q5_1.bin"),
|
| 97 |
+
Model(name: "base.en-q8_0", info: "(78 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en-q8_0.bin", filename: "base.en-q8_0.bin"),
|
| 98 |
+
Model(name: "small", info: "(F16, 466 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin", filename: "small.bin"),
|
| 99 |
+
Model(name: "small-q5_1", info: "(181 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small-q5_1.bin", filename: "small-q5_1.bin"),
|
| 100 |
+
Model(name: "small-q8_0", info: "(252 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small-q8_0.bin", filename: "small-q8_0.bin"),
|
| 101 |
+
Model(name: "small.en", info: "(F16, 466 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin", filename: "small.en.bin"),
|
| 102 |
+
Model(name: "small.en-q5_1", info: "(181 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en-q5_1.bin", filename: "small.en-q5_1.bin"),
|
| 103 |
+
Model(name: "small.en-q8_0", info: "(252 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en-q8_0.bin", filename: "small.en-q8_0.bin"),
|
| 104 |
+
Model(name: "medium", info: "(F16, 1.5 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin", filename: "medium.bin"),
|
| 105 |
+
Model(name: "medium-q5_0", info: "(514 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium-q5_0.bin", filename: "medium-q5_0.bin"),
|
| 106 |
+
Model(name: "medium-q8_0", info: "(785 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium-q8_0.bin", filename: "medium-q8_0.bin"),
|
| 107 |
+
Model(name: "medium.en", info: "(F16, 1.5 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.en.bin", filename: "medium.en.bin"),
|
| 108 |
+
Model(name: "medium.en-q5_0", info: "(514 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.en-q5_0.bin", filename: "medium.en-q5_0.bin"),
|
| 109 |
+
Model(name: "medium.en-q8_0", info: "(785 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.en-q8_0.bin", filename: "medium.en-q8_0.bin"),
|
| 110 |
+
Model(name: "large-v1", info: "(F16, 2.9 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large.bin", filename: "large.bin"),
|
| 111 |
+
Model(name: "large-v2", info: "(F16, 2.9 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v2.bin", filename: "large-v2.bin"),
|
| 112 |
+
Model(name: "large-v2-q5_0", info: "(1.1 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v2-q5_0.bin", filename: "large-v2-q5_0.bin"),
|
| 113 |
+
Model(name: "large-v2-q8_0", info: "(1.5 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v2-q8_0.bin", filename: "large-v2-q8_0.bin"),
|
| 114 |
+
Model(name: "large-v3", info: "(F16, 2.9 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.bin", filename: "large-v3.bin"),
|
| 115 |
+
Model(name: "large-v3-q5_0", info: "(1.1 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-q5_0.bin", filename: "large-v3-q5_0.bin"),
|
| 116 |
+
Model(name: "large-v3-turbo", info: "(F16, 1.5 GiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin", filename: "large-v3-turbo.bin"),
|
| 117 |
+
Model(name: "large-v3-turbo-q5_0", info: "(547 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo-q5_0.bin", filename: "large-v3-turbo-q5_0.bin"),
|
| 118 |
+
Model(name: "large-v3-turbo-q8_0", info: "(834 MiB)", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo-q8_0.bin", filename: "large-v3-turbo-q8_0.bin"),
|
| 119 |
+
]
|
| 120 |
+
|
| 121 |
+
static func getDownloadedModels() -> [Model] {
|
| 122 |
+
// Filter models that have been downloaded
|
| 123 |
+
return models.filter {
|
| 124 |
+
FileManager.default.fileExists(atPath: $0.fileURL.path())
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
func loadModel(model: Model) {
|
| 129 |
+
Task {
|
| 130 |
+
dismiss()
|
| 131 |
+
whisperState.loadModel(path: model.fileURL)
|
| 132 |
+
}
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
var body: some View {
|
| 136 |
+
List {
|
| 137 |
+
Section(header: Text("Models")) {
|
| 138 |
+
ForEach(ModelsView.models) { model in
|
| 139 |
+
DownloadButton(model: model)
|
| 140 |
+
.onLoad(perform: loadModel)
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
.listStyle(GroupedListStyle())
|
| 145 |
+
.navigationBarTitle("Models", displayMode: .inline).toolbar {}
|
| 146 |
+
}
|
| 147 |
}
|
| 148 |
}
|
| 149 |
+
|
| 150 |
+
//struct ContentView_Previews: PreviewProvider {
|
| 151 |
+
// static var previews: some View {
|
| 152 |
+
// ContentView()
|
| 153 |
+
// }
|
| 154 |
+
//}
|
examples/whisper.swiftui/whisper.swiftui.demo/UI/DownloadButton.swift
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import SwiftUI
|
| 2 |
+
|
| 3 |
+
struct DownloadButton: View {
|
| 4 |
+
private var model: Model
|
| 5 |
+
|
| 6 |
+
@State private var status: String
|
| 7 |
+
|
| 8 |
+
@State private var downloadTask: URLSessionDownloadTask?
|
| 9 |
+
@State private var progress = 0.0
|
| 10 |
+
@State private var observation: NSKeyValueObservation?
|
| 11 |
+
|
| 12 |
+
private var onLoad: ((_ model: Model) -> Void)?
|
| 13 |
+
|
| 14 |
+
init(model: Model) {
|
| 15 |
+
self.model = model
|
| 16 |
+
status = model.fileExists() ? "downloaded" : "download"
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
func onLoad(perform action: @escaping (_ model: Model) -> Void) -> DownloadButton {
|
| 20 |
+
var button = self
|
| 21 |
+
button.onLoad = action
|
| 22 |
+
return button
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
private func download() {
|
| 26 |
+
status = "downloading"
|
| 27 |
+
print("Downloading model \(model.name) from \(model.url)")
|
| 28 |
+
guard let url = URL(string: model.url) else { return }
|
| 29 |
+
|
| 30 |
+
downloadTask = URLSession.shared.downloadTask(with: url) { temporaryURL, response, error in
|
| 31 |
+
if let error = error {
|
| 32 |
+
print("Error: \(error.localizedDescription)")
|
| 33 |
+
return
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
|
| 37 |
+
print("Server error!")
|
| 38 |
+
return
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
do {
|
| 42 |
+
if let temporaryURL = temporaryURL {
|
| 43 |
+
try FileManager.default.copyItem(at: temporaryURL, to: model.fileURL)
|
| 44 |
+
print("Writing to \(model.filename) completed")
|
| 45 |
+
status = "downloaded"
|
| 46 |
+
}
|
| 47 |
+
} catch let err {
|
| 48 |
+
print("Error: \(err.localizedDescription)")
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
observation = downloadTask?.progress.observe(\.fractionCompleted) { progress, _ in
|
| 53 |
+
self.progress = progress.fractionCompleted
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
downloadTask?.resume()
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
var body: some View {
|
| 60 |
+
VStack {
|
| 61 |
+
Button(action: {
|
| 62 |
+
if (status == "download") {
|
| 63 |
+
download()
|
| 64 |
+
} else if (status == "downloading") {
|
| 65 |
+
downloadTask?.cancel()
|
| 66 |
+
status = "download"
|
| 67 |
+
} else if (status == "downloaded") {
|
| 68 |
+
if !model.fileExists() {
|
| 69 |
+
download()
|
| 70 |
+
}
|
| 71 |
+
onLoad?(model)
|
| 72 |
+
}
|
| 73 |
+
}) {
|
| 74 |
+
let title = "\(model.name) \(model.info)"
|
| 75 |
+
if (status == "download") {
|
| 76 |
+
Text("Download \(title)")
|
| 77 |
+
} else if (status == "downloading") {
|
| 78 |
+
Text("\(title) (Downloading \(Int(progress * 100))%)")
|
| 79 |
+
} else if (status == "downloaded") {
|
| 80 |
+
Text("Load \(title)")
|
| 81 |
+
} else {
|
| 82 |
+
Text("Unknown status")
|
| 83 |
+
}
|
| 84 |
+
}.swipeActions {
|
| 85 |
+
if (status == "downloaded") {
|
| 86 |
+
Button("Delete") {
|
| 87 |
+
do {
|
| 88 |
+
try FileManager.default.removeItem(at: model.fileURL)
|
| 89 |
+
} catch {
|
| 90 |
+
print("Error deleting file: \(error)")
|
| 91 |
+
}
|
| 92 |
+
status = "download"
|
| 93 |
+
}
|
| 94 |
+
.tint(.red)
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
.onDisappear() {
|
| 99 |
+
downloadTask?.cancel()
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
}
|
examples/whisper.swiftui/whisper.swiftui.xcodeproj/project.pbxproj
CHANGED
|
@@ -17,6 +17,8 @@
|
|
| 17 |
0AAC5D9F29539CD0003032C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0AAC5D9E29539CD0003032C3 /* Assets.xcassets */; };
|
| 18 |
0AAC5DCE2953A05C003032C3 /* WhisperState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AAC5DCD2953A05C003032C3 /* WhisperState.swift */; };
|
| 19 |
0AAC5DD12953A394003032C3 /* LibWhisper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AAC5DD02953A394003032C3 /* LibWhisper.swift */; };
|
|
|
|
|
|
|
| 20 |
E3F92DC52AFA8E3800A6A9D4 /* whisper in Frameworks */ = {isa = PBXBuildFile; productRef = E3F92DC42AFA8E3800A6A9D4 /* whisper */; };
|
| 21 |
/* End PBXBuildFile section */
|
| 22 |
|
|
@@ -33,6 +35,8 @@
|
|
| 33 |
0AAC5DA029539CD0003032C3 /* WhisperCppDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WhisperCppDemo.entitlements; sourceTree = "<group>"; };
|
| 34 |
0AAC5DCD2953A05C003032C3 /* WhisperState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhisperState.swift; sourceTree = "<group>"; };
|
| 35 |
0AAC5DD02953A394003032C3 /* LibWhisper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibWhisper.swift; sourceTree = "<group>"; };
|
|
|
|
|
|
|
| 36 |
E3F92DC22AFA8DD800A6A9D4 /* whisper.cpp */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = whisper.cpp; path = ../..; sourceTree = "<group>"; };
|
| 37 |
/* End PBXFileReference section */
|
| 38 |
|
|
@@ -52,6 +56,7 @@
|
|
| 52 |
isa = PBXGroup;
|
| 53 |
children = (
|
| 54 |
0AAC5DCD2953A05C003032C3 /* WhisperState.swift */,
|
|
|
|
| 55 |
);
|
| 56 |
path = Models;
|
| 57 |
sourceTree = "<group>";
|
|
@@ -119,6 +124,7 @@
|
|
| 119 |
isa = PBXGroup;
|
| 120 |
children = (
|
| 121 |
0AAC5D9C29539CCF003032C3 /* ContentView.swift */,
|
|
|
|
| 122 |
);
|
| 123 |
path = UI;
|
| 124 |
sourceTree = "<group>";
|
|
@@ -220,7 +226,9 @@
|
|
| 220 |
0AAC5DCE2953A05C003032C3 /* WhisperState.swift in Sources */,
|
| 221 |
0AAC5DD12953A394003032C3 /* LibWhisper.swift in Sources */,
|
| 222 |
0AA7514C2953B569001EE061 /* RiffWaveUtils.swift in Sources */,
|
|
|
|
| 223 |
0AA7514E2953D958001EE061 /* Recorder.swift in Sources */,
|
|
|
|
| 224 |
);
|
| 225 |
runOnlyForDeploymentPostprocessing = 0;
|
| 226 |
};
|
|
|
|
| 17 |
0AAC5D9F29539CD0003032C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0AAC5D9E29539CD0003032C3 /* Assets.xcassets */; };
|
| 18 |
0AAC5DCE2953A05C003032C3 /* WhisperState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AAC5DCD2953A05C003032C3 /* WhisperState.swift */; };
|
| 19 |
0AAC5DD12953A394003032C3 /* LibWhisper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AAC5DD02953A394003032C3 /* LibWhisper.swift */; };
|
| 20 |
+
7F79E0EE2CE0A78000ACD7BF /* DownloadButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F79E0ED2CE0A78000ACD7BF /* DownloadButton.swift */; };
|
| 21 |
+
7F79E0F02CE0C6F700ACD7BF /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F79E0EF2CE0C6F700ACD7BF /* Model.swift */; };
|
| 22 |
E3F92DC52AFA8E3800A6A9D4 /* whisper in Frameworks */ = {isa = PBXBuildFile; productRef = E3F92DC42AFA8E3800A6A9D4 /* whisper */; };
|
| 23 |
/* End PBXBuildFile section */
|
| 24 |
|
|
|
|
| 35 |
0AAC5DA029539CD0003032C3 /* WhisperCppDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WhisperCppDemo.entitlements; sourceTree = "<group>"; };
|
| 36 |
0AAC5DCD2953A05C003032C3 /* WhisperState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhisperState.swift; sourceTree = "<group>"; };
|
| 37 |
0AAC5DD02953A394003032C3 /* LibWhisper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibWhisper.swift; sourceTree = "<group>"; };
|
| 38 |
+
7F79E0ED2CE0A78000ACD7BF /* DownloadButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadButton.swift; sourceTree = "<group>"; };
|
| 39 |
+
7F79E0EF2CE0C6F700ACD7BF /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
|
| 40 |
E3F92DC22AFA8DD800A6A9D4 /* whisper.cpp */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = whisper.cpp; path = ../..; sourceTree = "<group>"; };
|
| 41 |
/* End PBXFileReference section */
|
| 42 |
|
|
|
|
| 56 |
isa = PBXGroup;
|
| 57 |
children = (
|
| 58 |
0AAC5DCD2953A05C003032C3 /* WhisperState.swift */,
|
| 59 |
+
7F79E0EF2CE0C6F700ACD7BF /* Model.swift */,
|
| 60 |
);
|
| 61 |
path = Models;
|
| 62 |
sourceTree = "<group>";
|
|
|
|
| 124 |
isa = PBXGroup;
|
| 125 |
children = (
|
| 126 |
0AAC5D9C29539CCF003032C3 /* ContentView.swift */,
|
| 127 |
+
7F79E0ED2CE0A78000ACD7BF /* DownloadButton.swift */,
|
| 128 |
);
|
| 129 |
path = UI;
|
| 130 |
sourceTree = "<group>";
|
|
|
|
| 226 |
0AAC5DCE2953A05C003032C3 /* WhisperState.swift in Sources */,
|
| 227 |
0AAC5DD12953A394003032C3 /* LibWhisper.swift in Sources */,
|
| 228 |
0AA7514C2953B569001EE061 /* RiffWaveUtils.swift in Sources */,
|
| 229 |
+
7F79E0EE2CE0A78000ACD7BF /* DownloadButton.swift in Sources */,
|
| 230 |
0AA7514E2953D958001EE061 /* Recorder.swift in Sources */,
|
| 231 |
+
7F79E0F02CE0C6F700ACD7BF /* Model.swift in Sources */,
|
| 232 |
);
|
| 233 |
runOnlyForDeploymentPostprocessing = 0;
|
| 234 |
};
|
include/whisper.h
CHANGED
|
@@ -423,6 +423,14 @@ extern "C" {
|
|
| 423 |
WHISPER_API whisper_token whisper_token_transcribe(struct whisper_context * ctx);
|
| 424 |
|
| 425 |
// Performance information from the default state.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
WHISPER_API void whisper_print_timings(struct whisper_context * ctx);
|
| 427 |
WHISPER_API void whisper_reset_timings(struct whisper_context * ctx);
|
| 428 |
|
|
|
|
| 423 |
WHISPER_API whisper_token whisper_token_transcribe(struct whisper_context * ctx);
|
| 424 |
|
| 425 |
// Performance information from the default state.
|
| 426 |
+
struct whisper_timings {
|
| 427 |
+
float sample_ms;
|
| 428 |
+
float encode_ms;
|
| 429 |
+
float decode_ms;
|
| 430 |
+
float batchd_ms;
|
| 431 |
+
float prompt_ms;
|
| 432 |
+
};
|
| 433 |
+
WHISPER_API struct whisper_timings * whisper_get_timings(struct whisper_context * ctx);
|
| 434 |
WHISPER_API void whisper_print_timings(struct whisper_context * ctx);
|
| 435 |
WHISPER_API void whisper_reset_timings(struct whisper_context * ctx);
|
| 436 |
|
src/whisper.cpp
CHANGED
|
@@ -4186,6 +4186,19 @@ whisper_token whisper_token_transcribe(struct whisper_context * ctx) {
|
|
| 4186 |
return ctx->vocab.token_transcribe;
|
| 4187 |
}
|
| 4188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4189 |
void whisper_print_timings(struct whisper_context * ctx) {
|
| 4190 |
const int64_t t_end_us = ggml_time_us();
|
| 4191 |
|
|
|
|
| 4186 |
return ctx->vocab.token_transcribe;
|
| 4187 |
}
|
| 4188 |
|
| 4189 |
+
struct whisper_timings * whisper_get_timings(struct whisper_context * ctx) {
|
| 4190 |
+
if (ctx->state == nullptr) {
|
| 4191 |
+
return nullptr;
|
| 4192 |
+
}
|
| 4193 |
+
whisper_timings * timings = new whisper_timings;
|
| 4194 |
+
timings->sample_ms = 1e-3f * ctx->state->t_sample_us / std::max(1, ctx->state->n_sample);
|
| 4195 |
+
timings->encode_ms = 1e-3f * ctx->state->t_encode_us / std::max(1, ctx->state->n_encode);
|
| 4196 |
+
timings->decode_ms = 1e-3f * ctx->state->t_decode_us / std::max(1, ctx->state->n_decode);
|
| 4197 |
+
timings->batchd_ms = 1e-3f * ctx->state->t_batchd_us / std::max(1, ctx->state->n_batchd);
|
| 4198 |
+
timings->prompt_ms = 1e-3f * ctx->state->t_prompt_us / std::max(1, ctx->state->n_prompt);
|
| 4199 |
+
return timings;
|
| 4200 |
+
}
|
| 4201 |
+
|
| 4202 |
void whisper_print_timings(struct whisper_context * ctx) {
|
| 4203 |
const int64_t t_end_us = ggml_time_us();
|
| 4204 |
|