From 53bb8284dc3f27d732bd5edc8c8b4a936a684d16 Mon Sep 17 00:00:00 2001 From: nell Date: Sat, 24 May 2025 10:01:23 +0900 Subject: [PATCH 1/6] Feat: add FrequencyAnalysisView. --- study/presentations/router/RouterView.swift | 1 + .../router/RouterViewState.swift | 1 + study/presentations/views/home/HomeView.swift | 3 +++ .../FrequencyAnalysisView.swift | 22 +++++++++++++++++++ .../FrequencyAnalysisViewModel.swift | 8 +++++++ .../FrequencyAnalysisViewState.swift | 5 +++++ 6 files changed, 40 insertions(+) create mode 100644 study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift create mode 100644 study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift create mode 100644 study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift diff --git a/study/presentations/router/RouterView.swift b/study/presentations/router/RouterView.swift index 5fd9a9e..7822001 100644 --- a/study/presentations/router/RouterView.swift +++ b/study/presentations/router/RouterView.swift @@ -28,6 +28,7 @@ struct RouterView: View { case .todo: TodoView() case .bucket: BucketView() case .prototypeSample: PrototypeSampleView() + case .frequencyAnalysis: FrequencyAnalysisView() } } .toolbarBackground(.hidden, for: .navigationBar) diff --git a/study/presentations/router/RouterViewState.swift b/study/presentations/router/RouterViewState.swift index 5a13370..2c9e446 100644 --- a/study/presentations/router/RouterViewState.swift +++ b/study/presentations/router/RouterViewState.swift @@ -10,6 +10,7 @@ enum SubPage { case todo case bucket case prototypeSample + case frequencyAnalysis } struct RouterViewState { diff --git a/study/presentations/views/home/HomeView.swift b/study/presentations/views/home/HomeView.swift index d17dcfd..131ccd3 100644 --- a/study/presentations/views/home/HomeView.swift +++ b/study/presentations/views/home/HomeView.swift @@ -32,6 +32,9 @@ struct HomeView: View { Tile(title: "Sample", subtitle: "Nickname") { router.push(.prototypeSample) } + Tile(title: "Frequency Analysis", subtitle: "Nell") { + router.push(.frequencyAnalysis) + } } } } diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift new file mode 100644 index 0000000..35a5f10 --- /dev/null +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift @@ -0,0 +1,22 @@ +// Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. + +import SwiftUI + +struct FrequencyAnalysisView: View { + var body: some View { + BaseView( + create: { FrequencyAnalysisViewModel() } + ) { viewModel, state in + VStack { + Toolbar(title: "Frequency Analysis") + Spacer() + } + } + } +} + +#Preview { + BasePreview { + FrequencyAnalysisView() + } +} diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift new file mode 100644 index 0000000..e9f5e55 --- /dev/null +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift @@ -0,0 +1,8 @@ +// Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. + +final class FrequencyAnalysisViewModel: BaseViewModel { + + init() { + super.init(state: .init()) + } +} diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift new file mode 100644 index 0000000..28fcf16 --- /dev/null +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift @@ -0,0 +1,5 @@ +// Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. + +struct FrequencyAnalysisViewState { + +} From 1ee975f85c0a2941bf5d121b0207b0d02487f680 Mon Sep 17 00:00:00 2001 From: nell Date: Sat, 24 May 2025 10:08:55 +0900 Subject: [PATCH 2/6] Chore: remove macos and vision os from target. --- study.xcodeproj/project.pbxproj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/study.xcodeproj/project.pbxproj b/study.xcodeproj/project.pbxproj index 7a25236..2f7dc7d 100644 --- a/study.xcodeproj/project.pbxproj +++ b/study.xcodeproj/project.pbxproj @@ -270,10 +270,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "ada-4th-c3.study"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,7"; + TARGETED_DEVICE_FAMILY = "1,2"; XROS_DEPLOYMENT_TARGET = 2.2; }; name = Debug; @@ -309,10 +310,11 @@ PRODUCT_BUNDLE_IDENTIFIER = "ada-4th-c3.study"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,7"; + TARGETED_DEVICE_FAMILY = "1,2"; XROS_DEPLOYMENT_TARGET = 2.2; }; name = Release; From fc467de454fb3da9372b5d39ceeacf798e3c2f2b Mon Sep 17 00:00:00 2001 From: nell Date: Sat, 24 May 2025 10:17:11 +0900 Subject: [PATCH 3/6] Chore: change base SDK to iOS. --- study.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/study.xcodeproj/project.pbxproj b/study.xcodeproj/project.pbxproj index 2f7dc7d..6ac7c4a 100644 --- a/study.xcodeproj/project.pbxproj +++ b/study.xcodeproj/project.pbxproj @@ -269,7 +269,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "ada-4th-c3.study"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; + SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; @@ -309,7 +309,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "ada-4th-c3.study"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; + SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; From 7e73ae4474f875ed6dcc00a821bbeab2e1edd0be Mon Sep 17 00:00:00 2001 From: nell Date: Sat, 24 May 2025 14:43:01 +0900 Subject: [PATCH 4/6] Feat: implementation of FFT-based frequency detection. --- study.xcodeproj/project.pbxproj | 8 +- study/core/base/BaseView.swift | 3 + study/core/base/BaseViewModel.swift | 2 + .../FrequencyAnalysisView.swift | 18 +-- .../FrequencyAnalysisViewModel.swift | 149 +++++++++++++++++- .../FrequencyAnalysisViewState.swift | 8 + .../audio/AudioManager.swift | 2 + .../views/splash/SplashView.swift | 2 +- 8 files changed, 179 insertions(+), 13 deletions(-) create mode 100644 study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift diff --git a/study.xcodeproj/project.pbxproj b/study.xcodeproj/project.pbxproj index 6ac7c4a..361f3e9 100644 --- a/study.xcodeproj/project.pbxproj +++ b/study.xcodeproj/project.pbxproj @@ -94,6 +94,8 @@ ); mainGroup = 2415A43F2DD0DF8E00EDC236; minimizedProjectReferenceProxies = 1; + packageReferences = ( + ); preferredProjectObjectVersion = 77; productRefGroup = 2415A4492DD0DF8E00EDC236 /* Products */; projectDirPath = ""; @@ -248,10 +250,11 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"study/assets\""; - DEVELOPMENT_TEAM = WL6LTCP7D2; + DEVELOPMENT_TEAM = P26KYG6RH6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "기타 소리를 듣고 피드백을 드리기 위해 마이크 접근 권한이 필요합니다."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -288,10 +291,11 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"study/assets\""; - DEVELOPMENT_TEAM = WL6LTCP7D2; + DEVELOPMENT_TEAM = P26KYG6RH6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "기타 소리를 듣고 피드백을 드리기 위해 마이크 접근 권한이 필요합니다."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; diff --git a/study/core/base/BaseView.swift b/study/core/base/BaseView.swift index 017179b..bfa3a73 100644 --- a/study/core/base/BaseView.swift +++ b/study/core/base/BaseView.swift @@ -28,5 +28,8 @@ struct BaseView>: Vie .navigationBarBackButtonHidden(navigationBarBackButtonHidden) .navigationBarHidden(navigationBarHidden) } + .onDisappear { + viewModel.dispose() + } } } diff --git a/study/core/base/BaseViewModel.swift b/study/core/base/BaseViewModel.swift index 3497488..f9a4748 100644 --- a/study/core/base/BaseViewModel.swift +++ b/study/core/base/BaseViewModel.swift @@ -18,4 +18,6 @@ class BaseViewModel: ObservableObject { } } } + + func dispose() {} } diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift index 35a5f10..5bd1f79 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift @@ -3,16 +3,16 @@ import SwiftUI struct FrequencyAnalysisView: View { - var body: some View { - BaseView( - create: { FrequencyAnalysisViewModel() } - ) { viewModel, state in - VStack { - Toolbar(title: "Frequency Analysis") - Spacer() - } - } + var body: some View { + BaseView( + create: { FrequencyAnalysisViewModel() } + ) { viewModel, state in + VStack { + Toolbar(title: "Frequency Analysis") + + } } + } } #Preview { diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift index e9f5e55..03b1b81 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift @@ -1,8 +1,155 @@ // Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. + +import SwiftUI +import AudioKit +import AVFoundation + +struct Complex { + var real: Double + var imag: Double + + init(_ real: Double, _ imag: Double) { + self.real = real + self.imag = imag + } + + static func + (lhs: Complex, rhs: Complex) -> Complex { + Complex(lhs.real + rhs.real, lhs.imag + rhs.imag) + } + + static func - (lhs: Complex, rhs: Complex) -> Complex { + Complex(lhs.real - rhs.real, lhs.imag - rhs.imag) + } + + static func * (lhs: Complex, rhs: Complex) -> Complex { + Complex(lhs.real * rhs.real - lhs.imag * rhs.imag, + lhs.real * rhs.imag + lhs.imag * rhs.real) + } +} + +func fft(_ input: [Complex]) -> [Complex] { + let n = input.count + if n == 1 { + return input + } + if n & (n - 1) != 0 { + fatalError("Input size must be a power of 2") + } + let even = fft((0.. String { + var minDiff = Double.infinity + var closestNote = "" + for note in noteFrequencies { + let diff = abs(note.freq - frequency) + if diff < minDiff { + minDiff = diff + closestNote = note.name + } + } + return closestNote + } + + func startRecording() { + inputNode = audioEngine.inputNode + let inputFormat = inputNode!.outputFormat(forBus: 0) + print("마이크 샘플링 주파수: \(inputFormat.sampleRate) Hz") + + inputNode?.installTap(onBus: 0, bufferSize: AVAudioFrameCount(windowSize), format: inputFormat) { buffer, time in + // Audio data in buffer + guard let channelData = buffer.floatChannelData?[0] else { return } + let frameLength = Int(buffer.frameLength) + + // RMS 계산 + var sumSquares: Double = 0 + for i in 0..= N { + let fftInput = Array(samples[0.. { + let audioManager = AudioManager() init() { - super.init(state: .init()) + super.init(state: .init( + amplitudes: Array(repeating: 0.0, count: 512), + dominantNote: "" + )) + audioManager.startRecording() + } + + override func dispose() { + audioManager.stopRecording() } } diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift index 28fcf16..a219396 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift @@ -1,5 +1,13 @@ // Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. struct FrequencyAnalysisViewState { + let amplitudes: [Float] + let dominantNote: String + func copy(amplitudes: [Float]? = nil, dominantNote: String? = nil) -> FrequencyAnalysisViewState { + return FrequencyAnalysisViewState( + amplitudes: amplitudes ?? self.amplitudes, + dominantNote: dominantNote ?? self.dominantNote + ) + } } diff --git a/study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift b/study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift new file mode 100644 index 0000000..9737e5b --- /dev/null +++ b/study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift @@ -0,0 +1,2 @@ +// Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. + diff --git a/study/presentations/views/splash/SplashView.swift b/study/presentations/views/splash/SplashView.swift index 52d3cdc..e8a7d0e 100644 --- a/study/presentations/views/splash/SplashView.swift +++ b/study/presentations/views/splash/SplashView.swift @@ -13,7 +13,7 @@ struct SplashView: View { .progressViewStyle(CircularProgressViewStyle()) .tint(.accentColor) .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { viewModel.onLoaded() router.setRoot(.home) } From 56d2b3fdc8d3f30c2ee3823d85ddc6af1fe7a99c Mon Sep 17 00:00:00 2001 From: nell Date: Mon, 26 May 2025 16:49:26 +0900 Subject: [PATCH 5/6] Feat: add HPS(Harmonic Prroduct Spectrum) algorithm to address harmonic issue. --- .../xcshareddata/xcschemes/study.xcscheme | 78 +++++++ .../features/audio/FestFourierTransform.swift | 29 +++ study/features/audio/Recorder.swift | 30 +++ study/features/audio/entities/Complex.swift | 27 +++ .../FrequencyAnalysisView.swift | 5 +- .../FrequencyAnalysisViewModel.swift | 221 +++++++++--------- .../FrequencyAnalysisViewState.swift | 8 +- .../audio/AudioManager.swift | 2 - 8 files changed, 277 insertions(+), 123 deletions(-) create mode 100644 study.xcodeproj/xcshareddata/xcschemes/study.xcscheme create mode 100644 study/features/audio/FestFourierTransform.swift create mode 100644 study/features/audio/Recorder.swift create mode 100644 study/features/audio/entities/Complex.swift delete mode 100644 study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift diff --git a/study.xcodeproj/xcshareddata/xcschemes/study.xcscheme b/study.xcodeproj/xcshareddata/xcschemes/study.xcscheme new file mode 100644 index 0000000..99ade34 --- /dev/null +++ b/study.xcodeproj/xcshareddata/xcschemes/study.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/study/features/audio/FestFourierTransform.swift b/study/features/audio/FestFourierTransform.swift new file mode 100644 index 0000000..df4517f --- /dev/null +++ b/study/features/audio/FestFourierTransform.swift @@ -0,0 +1,29 @@ +// Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. + +import AVFoundation + +// DFT(Discrete Fourier Transform)는 O(N^2) 걸림 +// FFT(Fest Fourier Transform)는 분할 정복으로 계산량을 O(NLogN)으로 줄임 +// 길이 n의 신호를 짝수 인덱스 샘플과 홀수 인덱스 샘플로 나눔 +class FestFourierTransform { + func run(_ input: [Complex]) -> [Complex] { + let n = input.count + if n == 1 { + return input + } + if n & (n - 1) != 0 { + fatalError("Input size must be a power of 2") + } + let even = run((0.. Complex { + Complex(lhs.real + rhs.real, lhs.imag + rhs.imag) + } + + static func - (lhs: Complex, rhs: Complex) -> Complex { + Complex(lhs.real - rhs.real, lhs.imag - rhs.imag) + } + + static func * (lhs: Complex, rhs: Complex) -> Complex { + Complex(lhs.real * rhs.real - lhs.imag * rhs.imag, + lhs.real * rhs.imag + lhs.imag * rhs.real) + } +} diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift index 5bd1f79..1ffe36d 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift @@ -9,7 +9,10 @@ struct FrequencyAnalysisView: View { ) { viewModel, state in VStack { Toolbar(title: "Frequency Analysis") - + Spacer() + Text(state.note) + .font(.title) + Spacer() } } } diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift index 03b1b81..859b84c 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift @@ -1,62 +1,21 @@ // Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. - import SwiftUI -import AudioKit import AVFoundation -struct Complex { - var real: Double - var imag: Double - - init(_ real: Double, _ imag: Double) { - self.real = real - self.imag = imag - } - - static func + (lhs: Complex, rhs: Complex) -> Complex { - Complex(lhs.real + rhs.real, lhs.imag + rhs.imag) - } - - static func - (lhs: Complex, rhs: Complex) -> Complex { - Complex(lhs.real - rhs.real, lhs.imag - rhs.imag) - } - - static func * (lhs: Complex, rhs: Complex) -> Complex { - Complex(lhs.real * rhs.real - lhs.imag * rhs.imag, - lhs.real * rhs.imag + lhs.imag * rhs.real) - } -} - -func fft(_ input: [Complex]) -> [Complex] { - let n = input.count - if n == 1 { - return input - } - if n & (n - 1) != 0 { - fatalError("Input size must be a power of 2") - } - let even = fft((0.. { + private let recorder = Recorder() - var output = Array(repeating: Complex(0,0), count: n) - for k in 0.. String { + private func getClosestNoteName(for frequency: Double) -> String { var minDiff = Double.infinity var closestNote = "" for note in noteFrequencies { @@ -79,77 +38,105 @@ class AudioManager: ObservableObject { return closestNote } - func startRecording() { - inputNode = audioEngine.inputNode - let inputFormat = inputNode!.outputFormat(forBus: 0) - print("마이크 샘플링 주파수: \(inputFormat.sampleRate) Hz") + init() { + super.init(state: .init( + amplitudes: Array(repeating: 0.0, count: 512), + note: "", + freq: 0 + )) + recorder.start(windowSize, recordHandler) + } + + override func dispose() { + recorder.stop() + } + + private func recordHandler(buffer: AVAudioPCMBuffer, time: AVAudioTime) { + // Audio data in buffer : 음압 + guard let channelData = buffer.floatChannelData?[0] else { return } + let frameLength = Int(buffer.frameLength) + + // RMS : 음수 값들을 모두 양수로 바꾸어 볼륨 판별 + var sumSquares: Double = 0 + for i in 0..= N { + let fftInput = Array(samples[0..= N { - let fftInput = Array(samples[0.. { - let audioManager = AudioManager() - - init() { - super.init(state: .init( - amplitudes: Array(repeating: 0.0, count: 512), - dominantNote: "" - )) - audioManager.startRecording() - } - - override func dispose() { - audioManager.stopRecording() } } diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift index a219396..646a3fa 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewState.swift @@ -2,12 +2,14 @@ struct FrequencyAnalysisViewState { let amplitudes: [Float] - let dominantNote: String + let note: String + let freq: Double - func copy(amplitudes: [Float]? = nil, dominantNote: String? = nil) -> FrequencyAnalysisViewState { + func copy(amplitudes: [Float]? = nil, note: String? = nil, freq: Double? = nil) -> FrequencyAnalysisViewState { return FrequencyAnalysisViewState( amplitudes: amplitudes ?? self.amplitudes, - dominantNote: dominantNote ?? self.dominantNote + note: note ?? self.note, + freq: freq ?? self.freq ) } } diff --git a/study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift b/study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift deleted file mode 100644 index 9737e5b..0000000 --- a/study/presentations/views/prototype/frequencyAnalysis/audio/AudioManager.swift +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. - From 6d2bb084adefae1fca2100e8bba3dd4dc221318c Mon Sep 17 00:00:00 2001 From: nell Date: Wed, 28 May 2025 09:25:29 +0900 Subject: [PATCH 6/6] Chore: run swiftformat. --- study/core/base/BaseViewModel.swift | 2 +- .../features/audio/FestFourierTransform.swift | 12 ++-- study/features/audio/Recorder.swift | 16 ++--- study/features/audio/entities/Complex.swift | 10 ++-- .../FrequencyAnalysisView.swift | 2 +- .../FrequencyAnalysisViewModel.swift | 60 +++++++++---------- .../FrequencyAnalysisViewState.swift | 2 +- 7 files changed, 52 insertions(+), 52 deletions(-) diff --git a/study/core/base/BaseViewModel.swift b/study/core/base/BaseViewModel.swift index f9a4748..4c8df9a 100644 --- a/study/core/base/BaseViewModel.swift +++ b/study/core/base/BaseViewModel.swift @@ -18,6 +18,6 @@ class BaseViewModel: ObservableObject { } } } - + func dispose() {} } diff --git a/study/features/audio/FestFourierTransform.swift b/study/features/audio/FestFourierTransform.swift index df4517f..92f7bb6 100644 --- a/study/features/audio/FestFourierTransform.swift +++ b/study/features/audio/FestFourierTransform.swift @@ -14,15 +14,15 @@ class FestFourierTransform { if n & (n - 1) != 0 { fatalError("Input size must be a power of 2") } - let even = run((0.. Complex { Complex(lhs.real + rhs.real, lhs.imag + rhs.imag) } - + static func - (lhs: Complex, rhs: Complex) -> Complex { Complex(lhs.real - rhs.real, lhs.imag - rhs.imag) } - + static func * (lhs: Complex, rhs: Complex) -> Complex { Complex(lhs.real * rhs.real - lhs.imag * rhs.imag, lhs.real * rhs.imag + lhs.imag * rhs.real) diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift index 1ffe36d..8a4ee65 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisView.swift @@ -6,7 +6,7 @@ struct FrequencyAnalysisView: View { var body: some View { BaseView( create: { FrequencyAnalysisViewModel() } - ) { viewModel, state in + ) { _, state in VStack { Toolbar(title: "Frequency Analysis") Spacer() diff --git a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift index 859b84c..eb52b99 100644 --- a/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift +++ b/study/presentations/views/prototype/frequencyAnalysis/FrequencyAnalysisViewModel.swift @@ -1,11 +1,11 @@ // Copyright © 2025 ADA 4th Challenge3 Team1. All rights reserved. -import SwiftUI import AVFoundation +import SwiftUI final class FrequencyAnalysisViewModel: BaseViewModel { private let recorder = Recorder() - + // FFT 입력 길이 // 오디오에서 몇 초 구간을 분석할지 결정 // 크기가 클수록 느리지만 정확도↑ @@ -13,18 +13,18 @@ final class FrequencyAnalysisViewModel: BaseViewModel String { var minDiff = Double.infinity var closestNote = "" @@ -37,7 +37,7 @@ final class FrequencyAnalysisViewModel: BaseViewModel= N { - let fftInput = Array(samples[0.. FrequencyAnalysisViewState { return FrequencyAnalysisViewState( amplitudes: amplitudes ?? self.amplitudes,