Skip to content

Commit

Permalink
Add audio device selector to transcribe + take a stab at Delete/Retry…
Browse files Browse the repository at this point in the history
… models (#54)

* Add audio devices to transcribe view

* Add delete and retry buttons

* Adjust mic device picker location

* RM retry

---------

Co-authored-by: ZachNagengast <znagengast@gmail.com>
  • Loading branch information
cgfarmer4 and ZachNagengast authored Mar 8, 2024
1 parent 7fcda51 commit ccdd77d
Showing 1 changed file with 100 additions and 48 deletions.
148 changes: 100 additions & 48 deletions Examples/WhisperAX/WhisperAX/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,16 @@ struct ContentView: View {
.progressViewStyle(CircularProgressViewStyle())
.scaleEffect(0.5)
}

Button(action: {
deleteModel()
}, label: {
Image(systemName: "trash")
})
.help("Delete model")
.buttonStyle(BorderlessButtonStyle())
.disabled(localModels.count == 0)
.disabled(!localModels.contains(selectedModel))

#if os(macOS)
Button(action: {
Expand Down Expand Up @@ -306,6 +316,33 @@ struct ContentView: View {
}

// MARK: - Controls
var audioDevicesView: some View {
Group {
#if os(macOS)
HStack {
if let audioDevices = audioDevices, audioDevices.count > 0 {
Picker("", selection: $selectedAudioInput) {
ForEach(audioDevices, id: \.self) { device in
Text(device.name).tag(device.name)
}
}
.frame(width: 250)
.disabled(isRecording)
}
}
.onAppear {
audioDevices = AudioProcessor.getAudioDevices()
if let audioDevices = audioDevices,
!audioDevices.isEmpty,
selectedAudioInput == "No Audio Input",
let device = audioDevices.first {
selectedAudioInput = device.name
}
}
#endif
}
}

var controlsView: some View {
VStack {
basicSettingsView
Expand All @@ -321,7 +358,13 @@ struct ContentView: View {
Label("Reset", systemImage: "arrow.clockwise")
}
.buttonStyle(.borderless)

Spacer()

audioDevicesView

Spacer()

Button {
showAdvancedOptions.toggle()
} label: {
Expand Down Expand Up @@ -395,63 +438,50 @@ struct ContentView: View {
}
}
case "Stream":
HStack {
Button {
resetState()
} label: {
Label("Reset", systemImage: "arrow.clockwise")
}
.frame(minWidth: 0, maxWidth: .infinity)
.buttonStyle(.borderless)

Button {
withAnimation {
toggleRecording(shouldLoop: true)
}
} label: {
Image(systemName: !isRecording ? "record.circle" : "stop.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 70, height: 70)
.padding()
.foregroundColor(modelState != .loaded ? .gray : .red)
}
.contentTransition(.symbolEffect(.replace))
.buttonStyle(BorderlessButtonStyle())
.disabled(modelState != .loaded)
.frame(minWidth: 0, maxWidth: .infinity)

VStack {
VStack {
HStack {
Button {
showAdvancedOptions.toggle()
resetState()
} label: {
Label("Settings", systemImage: "slider.horizontal.3")
Label("Reset", systemImage: "arrow.clockwise")
}
.frame(minWidth: 0, maxWidth: .infinity)
.buttonStyle(.borderless)

#if os(macOS)
HStack {
if let audioDevices = audioDevices, audioDevices.count > 0 {
Picker("", selection: $selectedAudioInput) {
ForEach(audioDevices, id: \.self) { device in
Text(device.name).tag(device.name)
}
}
.frame(minWidth: 80)
.disabled(isRecording)

Spacer()

audioDevicesView

Spacer()

VStack {
Button {
showAdvancedOptions.toggle()
} label: {
Label("Settings", systemImage: "slider.horizontal.3")
}
.frame(minWidth: 0, maxWidth: .infinity)
.buttonStyle(.borderless)
}
.onAppear {
audioDevices = AudioProcessor.getAudioDevices()
if let audioDevices = audioDevices,
!audioDevices.isEmpty,
selectedAudioInput == "No Audio Input",
let device = audioDevices.first {
selectedAudioInput = device.name
}

HStack {
Button {
withAnimation {
toggleRecording(shouldLoop: true)
}
} label: {
Image(systemName: !isRecording ? "record.circle" : "stop.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 70, height: 70)
.padding()
.foregroundColor(modelState != .loaded ? .gray : .red)
}
#endif
.contentTransition(.symbolEffect(.replace))
.buttonStyle(BorderlessButtonStyle())
.disabled(modelState != .loaded)
.frame(minWidth: 0, maxWidth: .infinity)
}
}
default:
Expand Down Expand Up @@ -779,13 +809,35 @@ struct ContentView: View {
try await whisperKit.loadModels()

await MainActor.run {
if !localModels.contains(model) {
localModels.append(model)
}

availableLanguages = whisperKit.tokenizer?.langauges.map { $0.key }.sorted() ?? ["english"]
loadingProgressValue = 1.0
modelState = whisperKit.modelState
}
}
}
}

func deleteModel() {
if localModels.contains(selectedModel) {
let modelFolder = URL(fileURLWithPath: localModelPath).appendingPathComponent("openai_whisper-\(selectedModel)")

do {
try FileManager.default.removeItem(at: modelFolder)

if let index = localModels.firstIndex(of: selectedModel) {
localModels.remove(at: index)
}

modelState = .unloaded
} catch {
print("Error deleting model: \(error)")
}
}
}

func updateProgressBar(targetProgress: Float, maxTime: TimeInterval) async {
let initialProgress = loadingProgressValue
Expand Down

0 comments on commit ccdd77d

Please sign in to comment.