Skip to content

Commit 9e8f14a

Browse files
authored
Merge branch 'develop' into fix/#65-LiveActivityFix
2 parents 9d0e4bf + e7159ba commit 9e8f14a

File tree

17 files changed

+572
-311
lines changed

17 files changed

+572
-311
lines changed

hearo/hearo.xcodeproj/project.pbxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@
10761076
BUNDLE_LOADER = "$(TEST_HOST)";
10771077
CODE_SIGN_STYLE = Automatic;
10781078
CURRENT_PROJECT_VERSION = 1;
1079-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1079+
DEVELOPMENT_TEAM = 2H928F4Z7L;
10801080
GENERATE_INFOPLIST_FILE = YES;
10811081
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
10821082
MARKETING_VERSION = 1.0;
@@ -1095,7 +1095,7 @@
10951095
BUNDLE_LOADER = "$(TEST_HOST)";
10961096
CODE_SIGN_STYLE = Automatic;
10971097
CURRENT_PROJECT_VERSION = 1;
1098-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1098+
DEVELOPMENT_TEAM = 2H928F4Z7L;
10991099
GENERATE_INFOPLIST_FILE = YES;
11001100
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
11011101
MARKETING_VERSION = 1.0;
@@ -1113,7 +1113,7 @@
11131113
buildSettings = {
11141114
CODE_SIGN_STYLE = Automatic;
11151115
CURRENT_PROJECT_VERSION = 1;
1116-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1116+
DEVELOPMENT_TEAM = 2H928F4Z7L;
11171117
GENERATE_INFOPLIST_FILE = YES;
11181118
MARKETING_VERSION = 1.0;
11191119
PRODUCT_BUNDLE_IDENTIFIER = pilgaaang.hearoUITests;
@@ -1130,7 +1130,7 @@
11301130
buildSettings = {
11311131
CODE_SIGN_STYLE = Automatic;
11321132
CURRENT_PROJECT_VERSION = 1;
1133-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1133+
DEVELOPMENT_TEAM = 2H928F4Z7L;
11341134
GENERATE_INFOPLIST_FILE = YES;
11351135
MARKETING_VERSION = 1.0;
11361136
PRODUCT_BUNDLE_IDENTIFIER = pilgaaang.hearoUITests;
@@ -1210,7 +1210,7 @@
12101210
BUNDLE_LOADER = "$(TEST_HOST)";
12111211
CODE_SIGN_STYLE = Automatic;
12121212
CURRENT_PROJECT_VERSION = 1;
1213-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1213+
DEVELOPMENT_TEAM = 2H928F4Z7L;
12141214
GENERATE_INFOPLIST_FILE = YES;
12151215
MARKETING_VERSION = 1.0;
12161216
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppTests";
@@ -1230,7 +1230,7 @@
12301230
BUNDLE_LOADER = "$(TEST_HOST)";
12311231
CODE_SIGN_STYLE = Automatic;
12321232
CURRENT_PROJECT_VERSION = 1;
1233-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1233+
DEVELOPMENT_TEAM = 2H928F4Z7L;
12341234
GENERATE_INFOPLIST_FILE = YES;
12351235
MARKETING_VERSION = 1.0;
12361236
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppTests";
@@ -1249,7 +1249,7 @@
12491249
buildSettings = {
12501250
CODE_SIGN_STYLE = Automatic;
12511251
CURRENT_PROJECT_VERSION = 1;
1252-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1252+
DEVELOPMENT_TEAM = 2H928F4Z7L;
12531253
GENERATE_INFOPLIST_FILE = YES;
12541254
MARKETING_VERSION = 1.0;
12551255
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppUITests";
@@ -1268,7 +1268,7 @@
12681268
buildSettings = {
12691269
CODE_SIGN_STYLE = Automatic;
12701270
CURRENT_PROJECT_VERSION = 1;
1271-
DEVELOPMENT_TEAM = J5N8Y9F8Z8;
1271+
DEVELOPMENT_TEAM = 2H928F4Z7L;
12721272
GENERATE_INFOPLIST_FILE = YES;
12731273
MARKETING_VERSION = 1.0;
12741274
PRODUCT_BUNDLE_IDENTIFIER = "pilgaaang.HearoadWatch-Watch-AppUITests";

hearo/hearo/Application/hearoApp.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
//
23
// hearoApp.swift
34
// hearo
@@ -171,8 +172,8 @@ struct ContentView: View {
171172
case .finish:
172173
FinishView(viewModel: FinishViewModel(appRootManager: appRootManager))
173174
case .warning:
174-
WarningView(appRootManager: appRootManager)
175-
}
175+
WarningView(viewModel: WarningViewModel(appRootManager: appRootManager))
176+
}
176177
}
177178
}
178179
}

hearo/hearo/Resources/Assets.xcassets/AccentColor.colorset/Contents.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
"colors" : [
33
{
44
"color" : {
5+
56
"color-space" : "srgb",
67
"components" : {
78
"alpha" : "1.000",
89
"blue" : "255",
910
"green" : "255",
1011
"red" : "255"
12+
1113
}
1214
},
1315
"idiom" : "universal"
@@ -20,12 +22,14 @@
2022
}
2123
],
2224
"color" : {
25+
2326
"color-space" : "srgb",
2427
"components" : {
2528
"alpha" : "1.000",
2629
"blue" : "255",
2730
"green" : "255",
2831
"red" : "255"
32+
2933
}
3034
},
3135
"idiom" : "universal"

hearo/hearo/Sources/Helper/Haptic.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,19 @@
88
import Foundation
99
import UIKit
1010

11+
// MARK: - Haptic 피드백 트리거
12+
func triggerHapticFeedback(for offset: CGFloat, targetOffset: CGFloat) {
13+
let intensity = min(max(offset / targetOffset, 0), 1.0) // 진동 강도 (0~1)
14+
let generator = UIImpactFeedbackGenerator(style: .light)
15+
generator.impactOccurred(intensity: CGFloat(intensity)) // 강도 기반 진동
16+
}
17+
18+
func triggerFinalHaptic() {
19+
let generator = UINotificationFeedbackGenerator()
20+
generator.notificationOccurred(.success) // 강한 성공 진동
21+
}
22+
23+
// MARK: - Predefined Haptic Feedback
1124
func triggerSuccessHaptic() {
1225
let generator = UINotificationFeedbackGenerator()
1326
generator.notificationOccurred(.success)
Binary file not shown.

hearo/hearo/Sources/Helper/HornSoundDetector.swift

Lines changed: 111 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,34 @@ class HornSoundDetector: NSObject, ObservableObject {
1919
private var inputNode: AVAudioInputNode!
2020
private var soundClassifier: HornSoundClassifier_V11?
2121
private var streamAnalyzer: SNAudioStreamAnalyzer?
22+
//fix 시작
23+
// @Published var isRecording = false
24+
// @Published var classificationResult = "녹음 시작 전"
25+
// @Published var detectedHornSound = false
26+
// @Published var topClassification: SNClassification? // 가장 높은 분류 저장
27+
// @Published var mlConfidences: [Double] = Array(repeating: 0.0, count: 4) // 각 채널의 신뢰도 배열
28+
// private var cancellables = Set<AnyCancellable>()
2229

30+
// override init() {
31+
//fix 끝
32+
private var appRootManager: AppRootManager // appRootManager 속성 추가
33+
2334
@Published var isRecording = false
2435
@Published var classificationResult = "녹음 시작 전"
25-
@Published var detectedHornSound = false
26-
@Published var topClassification: SNClassification? // 가장 높은 분류 저장
27-
@Published var mlConfidences: [Double] = Array(repeating: 0.0, count: 4) // 각 채널의 신뢰도 배열
2836
private var cancellables = Set<AnyCancellable>()
2937

30-
override init() {
38+
init(appRootManager: AppRootManager) {
39+
self.appRootManager = appRootManager
40+
3141
super.init()
3242
setupAudioSession()
3343
setupAudioEngine()
3444
setupSoundClassifier()
35-
checkNotificationPermission()
45+
//fix
46+
// checkNotificationPermission()
3647

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

@@ -47,11 +59,22 @@ class HornSoundDetector: NSObject, ObservableObject {
4759
print("오디오 세션 설정 실패: \(error)")
4860
}
4961
}
62+
//fix
63+
// private func setupAudioEngine() {
64+
// audioEngine = AVAudioEngine()
65+
// inputNode = audioEngine.inputNode
66+
// }
67+
68+
//fix
69+
5070
private func setupAudioEngine() {
5171
audioEngine = AVAudioEngine()
5272
inputNode = audioEngine.inputNode
73+
let format = inputNode.outputFormat(forBus: 0)
74+
let monoFormat = AVAudioFormat(commonFormat: format.commonFormat, sampleRate: format.sampleRate, channels: 1, interleaved: format.isInterleaved)
75+
streamAnalyzer = SNAudioStreamAnalyzer(format: monoFormat ?? format)
5376
}
54-
77+
5578
private func setupSoundClassifier() {
5679
do {
5780
let config = MLModelConfiguration()
@@ -62,59 +85,79 @@ class HornSoundDetector: NSObject, ObservableObject {
6285
print("소리 분류기 생성 실패: \(error)")
6386
}
6487
}
88+
//fix
6589

66-
private func checkNotificationPermission() {
67-
UNUserNotificationCenter.current().getNotificationSettings { settings in
68-
switch settings.authorizationStatus {
69-
case .notDetermined:
70-
self.requestNotificationPermission()
71-
case .denied:
72-
print("알림 권한이 거부되었습니다. 설정에서 권한을 변경해주세요.")
73-
case .authorized, .provisional, .ephemeral:
74-
print("알림 권한이 허용되었습니다.")
75-
@unknown default:
76-
break
77-
}
78-
}
79-
}
90+
// private func checkNotificationPermission() {
91+
// UNUserNotificationCenter.current().getNotificationSettings { settings in
92+
// switch settings.authorizationStatus {
93+
// case .notDetermined:
94+
// self.requestNotificationPermission()
95+
// case .denied:
96+
// print("알림 권한이 거부되었습니다. 설정에서 권한을 변경해주세요.")
97+
// case .authorized, .provisional, .ephemeral:
98+
// print("알림 권한이 허용되었습니다.")
99+
// @unknown default:
100+
// break
101+
// }
102+
// }
103+
// }
80104

81-
private func requestNotificationPermission() {
82-
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
83-
if granted {
84-
print("알림 권한이 허용되었습니다.")
85-
} else if let error = error {
86-
print("알림 권한 요청 중 오류 발생: \(error.localizedDescription)")
87-
}
88-
}
89-
}
105+
// private func requestNotificationPermission() {
106+
// UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
107+
// if granted {
108+
// print("알림 권한이 허용되었습니다.")
109+
// } else if let error = error {
110+
// print("알림 권한 요청 중 오류 발생: \(error.localizedDescription)")
111+
// }
112+
// }
113+
// }
90114

115+
//fix
116+
117+
91118
func startRecording() {
92119
guard !isRecording else {
93120
print("녹음이 이미 시작된 상태입니다.")
94121
return
95122
}
123+
//fix
96124

97-
let format = inputNode.outputFormat(forBus: 0)
98-
streamAnalyzer = SNAudioStreamAnalyzer(format: format)
125+
// let format = inputNode.outputFormat(forBus: 0)
126+
// streamAnalyzer = SNAudioStreamAnalyzer(format: format)
99127

100-
guard let streamAnalyzer = streamAnalyzer,
101-
let soundClassifier = soundClassifier else {
128+
// guard let streamAnalyzer = streamAnalyzer,
129+
// let soundClassifier = soundClassifier else {
130+
// print("스트림 분석기 또는 소리 분류기 생성 실패")
131+
// return
132+
// }
133+
//fix
134+
guard let streamAnalyzer = streamAnalyzer, let soundClassifier = soundClassifier else {
102135
print("스트림 분석기 또는 소리 분류기 생성 실패")
103136
return
104137
}
105-
138+
139+
106140
do {
107141
let request = try SNClassifySoundRequest(mlModel: soundClassifier.model)
108142
try streamAnalyzer.add(request, withObserver: self)
109143
} catch {
110144
print("분류 요청 생성 실패: \(error)")
111145
return
112146
}
147+
// //fix
148+
149+
// inputNode.installTap(onBus: 0, bufferSize: 8192, format: format) { [weak self] buffer, time in
150+
// self?.streamAnalyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
151+
// }
113152

153+
//fix
154+
155+
let format = inputNode.outputFormat(forBus: 0)
114156
inputNode.installTap(onBus: 0, bufferSize: 8192, format: format) { [weak self] buffer, time in
115157
self?.streamAnalyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
116158
}
117-
159+
160+
118161
audioEngine.prepare()
119162
do {
120163
try audioEngine.start()
@@ -125,7 +168,7 @@ class HornSoundDetector: NSObject, ObservableObject {
125168
print("오디오 엔진 시작 실패: \(error)")
126169
}
127170
}
128-
171+
129172
@objc func stopRecording() {
130173
guard isRecording else {
131174
print("녹음이 이미 중지된 상태입니다.")
@@ -138,9 +181,11 @@ class HornSoundDetector: NSObject, ObservableObject {
138181
isRecording = false
139182
print("오디오 엔진 중지됨")
140183
}
184+
141185

142186
func sendNotification(title: String, body: String) {
143187
print("알림 발송 시도")
188+
144189
let content = UNMutableNotificationContent()
145190
content.title = title
146191
content.body = body
@@ -161,25 +206,39 @@ extension HornSoundDetector: SNResultsObserving {
161206
func request(_ request: SNRequest, didProduce result: SNResult) {
162207
guard let result = result as? SNClassificationResult else { return }
163208

164-
let topClassifications = result.classifications.prefix(3)
209+
//fix
210+
// let topClassifications = result.classifications.prefix(3)
165211

166-
DispatchQueue.main.async {
167-
// 첫 번째 분류를 가장 신뢰도 높은 것으로 설정
168-
if let topClassification = topClassifications.first(where: { classification in
169-
return classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren"
170-
}) {
171-
self.topClassification = topClassification // 가장 높은 분류 저장
172-
self.classificationResult = topClassification.identifier // 소리 종류만 저장
173-
}
212+
// DispatchQueue.main.async {
213+
// // 첫 번째 분류를 가장 신뢰도 높은 것으로 설정
214+
// if let topClassification = topClassifications.first(where: { classification in
215+
// return classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren"
216+
// }) {
217+
// self.topClassification = topClassification // 가장 높은 분류 저장
218+
// self.classificationResult = topClassification.identifier // 소리 종류만 저장
219+
// }
174220

175-
for (index, classification) in topClassifications.enumerated() {
176-
if classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" {
177-
// 경적 및 사이렌 소리 감지
178-
if classification.confidence >= 1.0 {
179-
self.mlConfidences[index] = classification.confidence
180-
// 원하는 로직을 추가하세요
181-
}
221+
// for (index, classification) in topClassifications.enumerated() {
222+
// if classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" {
223+
// // 경적 및 사이렌 소리 감지
224+
// if classification.confidence >= 1.0 {
225+
// self.mlConfidences[index] = classification.confidence
226+
// // 원하는 로직을 추가하세요
227+
// }
228+
// }
229+
//fix
230+
DispatchQueue.main.async {
231+
if let topClassification = result.classifications.first, topClassification.confidence >= 1.0 {
232+
let isRelevantSound = ["Bicyclebell", "Carhorn", "Siren"].contains(topClassification.identifier)
233+
if isRelevantSound {
234+
self.classificationResult = topClassification.identifier
235+
self.appRootManager.detectedSound = topClassification.identifier
236+
self.appRootManager.currentRoot = .warning // 루트 변경
237+
print("감지된 소리: \(topClassification.identifier) - 신뢰도: \(topClassification.confidence)")
182238
}
239+
} else {
240+
print("신뢰도 부족 또는 관련 없는 소리 감지됨")
241+
183242
}
184243
}
185244
}

0 commit comments

Comments
 (0)