Hey I'm very inexperienced. So this might be embarrassing.
But I think I got the same issue as you and I can't fix it anyhow. Very frustrating, not Cursor, not Grok, not ChatGPT can help.
I tried to implement your solution, but it still wouldn't work. These are the error messages:
Error Messages
I think the Packages are added properly as well.
Packages
Do you see any reason why it might still not work?
import Foundation
import AVFoundation
import AudioKit
import AudioKitEX
@MainActor
class AudioManager: ObservableObject {
@Published var detectedNote: String = "No note detected"
@Published var detectedFrequency: Double = 0.0
@Published var audioLevel: Double = 0.0
@Published var spectrumData: [Double] = []
@Published var statusMessage: String = "Ready"
private var audioEngine: AudioEngine?
private var inputNode: Node?
private var pitchTap: PitchTap?
private var fftTap: FFTTap?
private var isListening = false
init() {
setupAudioKit()
}
private func setupAudioKit() {
// Initialize AudioKit engine
audioEngine = AudioEngine()
guard let audioEngine = audioEngine,
let input = audioEngine.input else {
statusMessage = "Failed to initialize audio input"
return
}
inputNode = input
statusMessage = "AudioKit initialized"
}
func startListening() {
guard !isListening else { return }
guard let audioEngine = audioEngine else {
statusMessage = "Audio engine not initialized"
return
}
// Request microphone permission
AVAudioApplication.requestRecordPermission { [weak self] granted in
DispatchQueue.main.async {
if granted {
self?.startAudioEngine()
} else {
self?.statusMessage = "Microphone permission denied"
}
}
}
}
private func startAudioEngine() {
guard let audioEngine = audioEngine,
let mic = audioEngine.input else {
statusMessage = "Audio components not initialized"
return
}
// Set a silence node as the engine's output to ensure it processes audio input
if let input = audioEngine.input {
let silence = Fader(input, gain: 0)
audioEngine.output = silence
} else {
statusMessage = "Failed to set silent output node"
return
}
// Use AudioKit's PitchTap for pitch detection
pitchTap = PitchTap(mic) { [weak self] pitch, amp in
DispatchQueue.main.async {
self?.audioLevel = Double(amp.first ?? 0)
if let frequency = pitch.first, frequency > 0 {
self?.detectedFrequency = frequency
self?.detectedNote = self?.frequencyToNote(frequency) ?? "Unknown"
} else {
self?.detectedNote = "No note detected"
self?.detectedFrequency = 0.0
}
}
}
pitchTap?.isActive = true
// Use AudioKit's FFTTap for spectrum analysis
fftTap = FFTTap(mic) { [weak self] fftData in
DispatchQueue.main.async {
self?.spectrumData = fftData.map { Double($0) }
}
}
fftTap?.isActive = true
do {
try audioEngine.start()
isListening = true
statusMessage = "Listening..."
} catch {
statusMessage = "Failed to start audio engine: \(error.localizedDescription)"
}
}
func stopListening() {
guard isListening else { return }
pitchTap?.isActive = false
fftTap?.isActive = false
audioEngine?.stop()
isListening = false
detectedNote = "No note detected"
detectedFrequency = 0.0
spectrumData = []
statusMessage = "Stopped"
}
private func frequencyToNote(_ frequency: Double) -> String {
// Standard A4 = 440Hz
let a4 = 440.0
let c0 = a4 * pow(2.0, -4.75)
guard frequency > 0 else { return "No note detected" }
// Calculate semitones from C0
let semitones = 12.0 * log2(frequency / c0)
let octave = Int(semitones / 12.0)
let noteIndex = Int(round(semitones)) % 12
// Safe array access with bounds checking
let safeIndex = (noteIndex + 12) % 12 // Always positive
let noteNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
guard safeIndex >= 0 && safeIndex < noteNames.count else {
return "Unknown"
}
let noteName = noteNames[safeIndex]
return "\(noteName)\(octave)"
}
}