Skip to content

Commit

Permalink
Merge branch 'develop' into fix/#65-LiveActivityFix
Browse files Browse the repository at this point in the history
  • Loading branch information
KimPilGyeom authored Nov 11, 2024
2 parents 9d0e4bf + e7159ba commit 9e8f14a
Show file tree
Hide file tree
Showing 17 changed files with 572 additions and 311 deletions.
16 changes: 8 additions & 8 deletions hearo/hearo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
MARKETING_VERSION = 1.0;
Expand All @@ -1095,7 +1095,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
MARKETING_VERSION = 1.0;
Expand All @@ -1113,7 +1113,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = pilgaaang.hearoUITests;
Expand All @@ -1130,7 +1130,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = pilgaaang.hearoUITests;
Expand Down Expand Up @@ -1210,7 +1210,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppTests";
Expand All @@ -1230,7 +1230,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppTests";
Expand All @@ -1249,7 +1249,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppUITests";
Expand All @@ -1268,7 +1268,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
DEVELOPMENT_TEAM = 2H928F4Z7L;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppUITests";
Expand Down
5 changes: 3 additions & 2 deletions hearo/hearo/Application/hearoApp.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

//
// hearoApp.swift
// hearo
Expand Down Expand Up @@ -171,8 +172,8 @@ struct ContentView: View {
case .finish:
FinishView(viewModel: FinishViewModel(appRootManager: appRootManager))
case .warning:
WarningView(appRootManager: appRootManager)
}
WarningView(viewModel: WarningViewModel(appRootManager: appRootManager))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
"colors" : [
{
"color" : {

"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "255",
"green" : "255",
"red" : "255"

}
},
"idiom" : "universal"
Expand All @@ -20,12 +22,14 @@
}
],
"color" : {

"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "255",
"green" : "255",
"red" : "255"

}
},
"idiom" : "universal"
Expand Down
13 changes: 13 additions & 0 deletions hearo/hearo/Sources/Helper/Haptic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@
import Foundation
import UIKit

// MARK: - Haptic 피드백 트리거
func triggerHapticFeedback(for offset: CGFloat, targetOffset: CGFloat) {
let intensity = min(max(offset / targetOffset, 0), 1.0) // 진동 강도 (0~1)
let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred(intensity: CGFloat(intensity)) // 강도 기반 진동
}

func triggerFinalHaptic() {
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success) // 강한 성공 진동
}

// MARK: - Predefined Haptic Feedback
func triggerSuccessHaptic() {
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success)
Expand Down
Binary file not shown.
163 changes: 111 additions & 52 deletions hearo/hearo/Sources/Helper/HornSoundDetector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,34 @@ class HornSoundDetector: NSObject, ObservableObject {
private var inputNode: AVAudioInputNode!
private var soundClassifier: HornSoundClassifier_V11?
private var streamAnalyzer: SNAudioStreamAnalyzer?
//fix 시작
// @Published var isRecording = false
// @Published var classificationResult = "녹음 시작 전"
// @Published var detectedHornSound = false
// @Published var topClassification: SNClassification? // 가장 높은 분류 저장
// @Published var mlConfidences: [Double] = Array(repeating: 0.0, count: 4) // 각 채널의 신뢰도 배열
// private var cancellables = Set<AnyCancellable>()

// override init() {
//fix 끝
private var appRootManager: AppRootManager // appRootManager 속성 추가

@Published var isRecording = false
@Published var classificationResult = "녹음 시작 전"
@Published var detectedHornSound = false
@Published var topClassification: SNClassification? // 가장 높은 분류 저장
@Published var mlConfidences: [Double] = Array(repeating: 0.0, count: 4) // 각 채널의 신뢰도 배열
private var cancellables = Set<AnyCancellable>()

override init() {
init(appRootManager: AppRootManager) {
self.appRootManager = appRootManager

super.init()
setupAudioSession()
setupAudioEngine()
setupSoundClassifier()
checkNotificationPermission()
//fix
// checkNotificationPermission()

// 앱이 백그라운드로 전환될 때 녹음을 중지하도록 옵저버 설정
//fix
NotificationCenter.default.addObserver(self, selector: #selector(stopRecording), name: UIApplication.didEnterBackgroundNotification, object: nil)
}

Expand All @@ -47,11 +59,22 @@ class HornSoundDetector: NSObject, ObservableObject {
print("오디오 세션 설정 실패: \(error)")
}
}
//fix
// private func setupAudioEngine() {
// audioEngine = AVAudioEngine()
// inputNode = audioEngine.inputNode
// }

//fix

private func setupAudioEngine() {
audioEngine = AVAudioEngine()
inputNode = audioEngine.inputNode
let format = inputNode.outputFormat(forBus: 0)
let monoFormat = AVAudioFormat(commonFormat: format.commonFormat, sampleRate: format.sampleRate, channels: 1, interleaved: format.isInterleaved)
streamAnalyzer = SNAudioStreamAnalyzer(format: monoFormat ?? format)
}

private func setupSoundClassifier() {
do {
let config = MLModelConfiguration()
Expand All @@ -62,59 +85,79 @@ class HornSoundDetector: NSObject, ObservableObject {
print("소리 분류기 생성 실패: \(error)")
}
}
//fix

private func checkNotificationPermission() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
switch settings.authorizationStatus {
case .notDetermined:
self.requestNotificationPermission()
case .denied:
print("알림 권한이 거부되었습니다. 설정에서 권한을 변경해주세요.")
case .authorized, .provisional, .ephemeral:
print("알림 권한이 허용되었습니다.")
@unknown default:
break
}
}
}
// private func checkNotificationPermission() {
// UNUserNotificationCenter.current().getNotificationSettings { settings in
// switch settings.authorizationStatus {
// case .notDetermined:
// self.requestNotificationPermission()
// case .denied:
// print("알림 권한이 거부되었습니다. 설정에서 권한을 변경해주세요.")
// case .authorized, .provisional, .ephemeral:
// print("알림 권한이 허용되었습니다.")
// @unknown default:
// break
// }
// }
// }

private func requestNotificationPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
if granted {
print("알림 권한이 허용되었습니다.")
} else if let error = error {
print("알림 권한 요청 중 오류 발생: \(error.localizedDescription)")
}
}
}
// private func requestNotificationPermission() {
// UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
// if granted {
// print("알림 권한이 허용되었습니다.")
// } else if let error = error {
// print("알림 권한 요청 중 오류 발생: \(error.localizedDescription)")
// }
// }
// }

//fix


func startRecording() {
guard !isRecording else {
print("녹음이 이미 시작된 상태입니다.")
return
}
//fix

let format = inputNode.outputFormat(forBus: 0)
streamAnalyzer = SNAudioStreamAnalyzer(format: format)
// let format = inputNode.outputFormat(forBus: 0)
// streamAnalyzer = SNAudioStreamAnalyzer(format: format)

guard let streamAnalyzer = streamAnalyzer,
let soundClassifier = soundClassifier else {
// guard let streamAnalyzer = streamAnalyzer,
// let soundClassifier = soundClassifier else {
// print("스트림 분석기 또는 소리 분류기 생성 실패")
// return
// }
//fix
guard let streamAnalyzer = streamAnalyzer, let soundClassifier = soundClassifier else {
print("스트림 분석기 또는 소리 분류기 생성 실패")
return
}



do {
let request = try SNClassifySoundRequest(mlModel: soundClassifier.model)
try streamAnalyzer.add(request, withObserver: self)
} catch {
print("분류 요청 생성 실패: \(error)")
return
}
// //fix

// inputNode.installTap(onBus: 0, bufferSize: 8192, format: format) { [weak self] buffer, time in
// self?.streamAnalyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
// }

//fix

let format = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 8192, format: format) { [weak self] buffer, time in
self?.streamAnalyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
}



audioEngine.prepare()
do {
try audioEngine.start()
Expand All @@ -125,7 +168,7 @@ class HornSoundDetector: NSObject, ObservableObject {
print("오디오 엔진 시작 실패: \(error)")
}
}

@objc func stopRecording() {
guard isRecording else {
print("녹음이 이미 중지된 상태입니다.")
Expand All @@ -138,9 +181,11 @@ class HornSoundDetector: NSObject, ObservableObject {
isRecording = false
print("오디오 엔진 중지됨")
}


func sendNotification(title: String, body: String) {
print("알림 발송 시도")

let content = UNMutableNotificationContent()
content.title = title
content.body = body
Expand All @@ -161,25 +206,39 @@ extension HornSoundDetector: SNResultsObserving {
func request(_ request: SNRequest, didProduce result: SNResult) {
guard let result = result as? SNClassificationResult else { return }

let topClassifications = result.classifications.prefix(3)
//fix
// let topClassifications = result.classifications.prefix(3)

DispatchQueue.main.async {
// 첫 번째 분류를 가장 신뢰도 높은 것으로 설정
if let topClassification = topClassifications.first(where: { classification in
return classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren"
}) {
self.topClassification = topClassification // 가장 높은 분류 저장
self.classificationResult = topClassification.identifier // 소리 종류만 저장
}
// DispatchQueue.main.async {
// // 첫 번째 분류를 가장 신뢰도 높은 것으로 설정
// if let topClassification = topClassifications.first(where: { classification in
// return classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren"
// }) {
// self.topClassification = topClassification // 가장 높은 분류 저장
// self.classificationResult = topClassification.identifier // 소리 종류만 저장
// }

for (index, classification) in topClassifications.enumerated() {
if classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" {
// 경적 및 사이렌 소리 감지
if classification.confidence >= 1.0 {
self.mlConfidences[index] = classification.confidence
// 원하는 로직을 추가하세요
}
// for (index, classification) in topClassifications.enumerated() {
// if classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" {
// // 경적 및 사이렌 소리 감지
// if classification.confidence >= 1.0 {
// self.mlConfidences[index] = classification.confidence
// // 원하는 로직을 추가하세요
// }
// }
//fix
DispatchQueue.main.async {
if let topClassification = result.classifications.first, topClassification.confidence >= 1.0 {
let isRelevantSound = ["Bicyclebell", "Carhorn", "Siren"].contains(topClassification.identifier)
if isRelevantSound {
self.classificationResult = topClassification.identifier
self.appRootManager.detectedSound = topClassification.identifier
self.appRootManager.currentRoot = .warning // 루트 변경
print("감지된 소리: \(topClassification.identifier) - 신뢰도: \(topClassification.confidence)")
}
} else {
print("신뢰도 부족 또는 관련 없는 소리 감지됨")

}
}
}
Expand Down
Loading

0 comments on commit 9e8f14a

Please sign in to comment.