Skip to content

Commit 777c1a8

Browse files
๐Ÿ› [Fix]: ์žฌ์ƒ ๊ด€๋ จ ์˜ค๋ฅ˜ ์ˆ˜์ • #447 #448 #449 #450 (#451)
* ๐Ÿ›[Fix]: Fix Image Name * ํ—ค๋“œํฐ ์ด๋ฏธ์ง€์˜ ์ด๋ฆ„์„ headPhone -> HeadPhone์œผ๋กœ ๋ณ€๊ฒฝ * ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ํ•ด๋‹น ์ด๋ฏธ์ง€๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š๋Š” ํ˜„์ƒ์ด ํ•ด๊ฒฐ๋จ * ๐Ÿ› ๏ธ [Chore]: ์˜จ๋ณด๋”ฉ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ด๋ฆ„ ์ „์ฒด ๋ณ€๊ฒฝ * ํŒŒ์ธ๋”์™€ Xcode๋‚ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฏธ์ง€ ์ด๋ฆ„์„ ๋™์ผํ•˜๊ฒŒ ๋ณ€๊ฒฝ * ๐Ÿ—‘๏ธ [Del]: Assets * โž•[Add]: Assets * ๐Ÿ›[Fix]: ์ด์ „ ๊ณก ์žฌ์ƒ, ๋‹ค์Œ ๊ณก ์žฌ์ƒ์‹œ play ๋ฒ„ํŠผ ์˜ค๋ฅ˜ * ๐Ÿ›[Fix]: ListenListCell ํ„ฐ์น˜ํ•˜๋ฉด ์žฌ์ƒ๋˜๋„๋ก ๋ณ€๊ฒฝ * ๐Ÿ› [Fix]: แ„€แ…กแ†ซแ„€แ…งแ†จ แ„Œแ…ฉแ„Œแ…ฅแ†ฏแ„‰แ…ต แ„‹แ…ตแ†ฏแ„‡แ…ฎ แ„‹แ…ณแ†ทแ„‹แ…ฏแ†ซแ„‹แ…ต แ„Œแ…ฅแ†ซแ„Žแ…ฆ แ„Œแ…ขแ„‰แ…ขแ†ผแ„ƒแ…ฌแ„Œแ…ต แ„‹แ…กแ†ญแ„‚แ…ณแ†ซ แ„†แ…ฎแ†ซแ„Œแ…ฆ แ„’แ…ขแ„€แ…งแ†ฏ * ๐Ÿ› [Fix]: ์•„๋ฌด๋Ÿฐ ์ง€ํ‘œ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ์Œ์›์ด ์ €์žฅ๋˜๋„๋ก ์ˆ˜์ • * ๐Ÿ—‘๏ธ [Del]: ๋ถˆํ•„์š”ํ•œ init ์ฝ”๋“œ ์ œ๊ฑฐ * โœ๏ธ[Comment]: MARK ์ฃผ์„ ์ถ”๊ฐ€ * ๐Ÿช„[Style]: ๋ช…์‹œ์ ์œผ๋กœ ๋ณ€๊ฒฝ - toggle -> true & false --------- Co-authored-by: hamsik22 <[email protected]>
1 parent 9607418 commit 777c1a8

File tree

8 files changed

+106
-34
lines changed

8 files changed

+106
-34
lines changed

โ€ŽRelaxOn.xcodeproj/xcshareddata/xcschemes/RelaxOn.xcschemeโ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
</Testables>
4242
</TestAction>
4343
<LaunchAction
44-
buildConfiguration = "Debug"
44+
buildConfiguration = "Release"
4545
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
4646
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
4747
launchStyle = "0"

โ€ŽRelaxOn/App/Manager/AudioEngineManager.swiftโ€Ž

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import AVFoundation
99
import Combine
1010

1111
final class AudioEngineManager: ObservableObject {
12+
13+
// MARK: - Properties
1214
static let shared = AudioEngineManager()
1315

1416
var engine = AVAudioEngine()
@@ -22,11 +24,8 @@ final class AudioEngineManager: ObservableObject {
2224
private var intervalCancellable: AnyCancellable?
2325
private var timerSubscription: Cancellable?
2426

25-
@Published var interval: Double = 1.0 {
26-
didSet {
27-
scheduleNextBuffer()
28-
}
29-
}
27+
@Published private var currentPlayingSound: Playable?
28+
@Published var interval: Double = 1.0
3029

3130
@Published var pitch: Double = 0 {
3231
didSet {
@@ -48,6 +47,7 @@ final class AudioEngineManager: ObservableObject {
4847
}
4948
}
5049

50+
// MARK: - Initialization
5151
private init() {
5252
setupAudioSession()
5353
setupEngine()
@@ -74,11 +74,19 @@ extension AudioEngineManager {
7474
}
7575

7676
private func setupConnections() {
77+
if engine.isRunning {
78+
engine.stop()
79+
}
7780
if let audioFile = audioFile {
7881
engine.connect(player, to: pitchEffect, format: audioFile.processingFormat)
7982
engine.connect(pitchEffect, to: volumeEffect, format: audioFile.processingFormat)
8083
engine.connect(volumeEffect, to: engine.mainMixerNode, format: audioFile.processingFormat)
8184
}
85+
do {
86+
try engine.start()
87+
} catch {
88+
print("Unable to start engine: \(error.localizedDescription)")
89+
}
8290
}
8391

8492
/**
@@ -90,6 +98,7 @@ extension AudioEngineManager {
9098
.removeDuplicates()
9199
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
92100
.sink { [weak self] _ in
101+
self?.timerSubscription?.cancel()
93102
self?.scheduleNextBuffer()
94103
}
95104
}
@@ -102,6 +111,8 @@ extension AudioEngineManager {
102111
func play<T: Playable>(with sound: T) {
103112
print(#function)
104113

114+
currentPlayingSound = sound
115+
105116
let targetFile = sound.filter.rawValue
106117
guard let fileURL = Bundle.main.url(forResource: targetFile, withExtension: MusicExtension.mp3.rawValue) else {
107118
print("File not found")
@@ -117,7 +128,7 @@ extension AudioEngineManager {
117128
audioVariation = customSound.audioVariation
118129
}
119130

120-
scheduleNextBuffer()
131+
scheduleNextBuffer(with: sound)
121132

122133
} catch {
123134
print(error.localizedDescription)
@@ -159,17 +170,27 @@ extension AudioEngineManager {
159170
๋‹ค์Œ ์˜ค๋””์˜ค ๋ฒ„ํผ๋ฅผ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค. ์ค€๋น„๋œ ๋ฒ„ํผ๋ฅผ ์‚ฌ์šฉํ•ด ์˜ค๋””์˜ค๋ฅผ ์žฌ์ƒํ•˜๊ณ , ์ฃผ์–ด์ง„ ์ธํ„ฐ๋ฒŒ์— ๋”ฐ๋ผ ๋‹ค์Œ ๋ฒ„ํผ ์žฌ์ƒ์„ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค.
160171
Combine ํ”„๋ ˆ์ž„์›Œํฌ์˜ Timer.publish๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์ •๋œ ์ธํ„ฐ๋ฒŒ๋งˆ๋‹ค ๋ฒ„ํผ ์žฌ์ƒ์„ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.
161172
*/
162-
private func scheduleNextBuffer() {
173+
private func scheduleNextBuffer(with playingSound: Playable? = nil) {
163174
print(#function)
164175

165176
guard let buffer = audioBuffer else {
166177
print("Failed to prepare buffer")
167178
return
168179
}
169180

181+
guard let sound = playingSound ?? currentPlayingSound else {
182+
print("No playing sound")
183+
return
184+
}
185+
170186
// ์ทจ์†Œ ๊ฐ€๋Šฅํ•œ ํƒ€์ด๋จธ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
171187
timerSubscription?.cancel()
172-
timerSubscription = Timer.publish(every: interval, on: RunLoop.main, in: .common)
188+
189+
timerSubscription = Timer.publish(
190+
every: interval + sound.filter.duration,
191+
on: RunLoop.main,
192+
in: .common
193+
)
173194
.autoconnect()
174195
.sink { [weak self] _ in
175196
guard let self = self else { return }

โ€ŽRelaxOn/App/Manager/UserDefaultsManager.swiftโ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import Foundation
1111
UserDefaults์— ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅํ•œ ์‚ฌ์šด๋“œ ์ •๋ณด & ์˜จ๋ณด๋”ฉ ์—ฌ๋ถ€ & ๋งˆ์ง€๋ง‰ ์žฌ์ƒ ์‚ฌ์šด๋“œ ์ •๋ณด
1212
*/
1313
final class UserDefaultsManager {
14+
15+
// MARK: - Properties
1416
static let shared = UserDefaultsManager()
1517
private let standard = UserDefaults.standard
1618
private let CUSTOM_SOUND_KEY = UserDefaults.Keys.customSound
1719
private let IS_FIRST_VISIT = UserDefaults.Keys.isFirstVisit
1820
private let LAST_SOUND_KEY = UserDefaults.Keys.lastPlayedSoundKey
21+
1922
}
2023

2124
// MARK: - Data Get, Set Properties
@@ -73,6 +76,11 @@ extension UserDefaultsManager {
7376
standard.set(data, forKey: LAST_SOUND_KEY)
7477
}
7578
}
79+
80+
}
81+
82+
// MARK: - Methods
83+
extension UserDefaultsManager {
7684

7785
func removeOneCustomSound(at index: Int) {
7886
var customSounds = self.customSounds
@@ -83,4 +91,5 @@ extension UserDefaultsManager {
8391
func removeAllCustomSounds() {
8492
standard.removeObject(forKey: CUSTOM_SOUND_KEY)
8593
}
94+
8695
}

โ€ŽRelaxOn/App/Manager/UserFileManager.swiftโ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ import Foundation
1111
FileManager์— ์˜ค๋””์˜ค ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ผ๋กœ ์ €์žฅ & ๋ถˆ๋Ÿฌ์˜ค๊ธฐ & (ํŒŒ์ผ์ด๋ฆ„)์ˆ˜์ • & ํŒŒ์ผ ์‚ญ์ œ ๊ธฐ๋Šฅ
1212
*/
1313
final class UserFileManager {
14+
15+
// MARK: - Properties
1416
static let shared = UserFileManager()
1517
private let fileManager = FileManager.default
18+
1619
}
1720

21+
// MARK: - Methods
1822
extension UserFileManager {
1923

2024
func saveCustomSound(_ customSound: CustomSound) -> Bool {

โ€ŽRelaxOn/App/Models/AudioVariation.swiftโ€Ž

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,7 @@ struct AudioVariation: Codable {
1515
var pitch: Float
1616
var interval: Float
1717

18-
init() {
19-
self.volume = 0.5
20-
self.pitch = 0
21-
self.interval = 1.0
22-
}
23-
24-
init(volume: Float, pitch: Float, interval: Float = 1.0) {
18+
init(volume: Float = 0.5, pitch: Float = 0, interval: Float = 1.0) {
2519
self.volume = volume
2620
self.pitch = pitch
2721
self.interval = interval

โ€ŽRelaxOn/App/Models/CustomSound.swiftโ€Ž

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,13 @@ struct CustomSound: Identifiable, Codable, Equatable, Playable {
1919
var filter: AudioFilter
2020
var color: String
2121

22-
init() {
23-
self.id = UUID()
24-
self.title = "์ €์žฅ๋œ ์Œ์›์ด ์—†์Šต๋‹ˆ๋‹ค."
25-
self.category = .none
26-
self.audioVariation = AudioVariation()
27-
self.filter = .none
28-
self.color = SoundCategory.none.defaultColor
29-
}
30-
31-
init(title: String, category: SoundCategory, variation: AudioVariation, filter: AudioFilter, color: String = "") {
22+
init(
23+
title: String = "์ €์žฅ๋œ ์Œ์›์ด ์—†์Šต๋‹ˆ๋‹ค.",
24+
category: SoundCategory = .none,
25+
variation: AudioVariation = AudioVariation(),
26+
filter: AudioFilter = .none,
27+
color: String = ""
28+
) {
3229
self.id = UUID()
3330
self.title = title
3431
self.category = category
@@ -48,6 +45,46 @@ enum AudioFilter: String, Codable {
4845
case WaterDrop, Basement, Cave, Pipe, Sink
4946
case SingingBowl, Focus, Training, Empty, Vibration
5047
case Bird, Owl, Woodpecker, Forest, Cuckoo
48+
49+
var duration: TimeInterval {
50+
switch self {
51+
case .none:
52+
return 0.0
53+
54+
case .WaterDrop:
55+
return 1.0
56+
case .Basement:
57+
return 2.0
58+
case .Cave:
59+
return 2.0
60+
case .Pipe:
61+
return 3.0
62+
case .Sink:
63+
return 2.0
64+
65+
case .SingingBowl:
66+
return 5.0
67+
case .Focus:
68+
return 5.0
69+
case .Training:
70+
return 4.0
71+
case .Empty:
72+
return 5.0
73+
case .Vibration:
74+
return 7.0
75+
76+
case .Bird:
77+
return 1.0
78+
case .Owl:
79+
return 3.0
80+
case .Woodpecker:
81+
return 4.0
82+
case .Forest:
83+
return 4.0
84+
case .Cuckoo:
85+
return 3.0
86+
}
87+
}
5188
}
5289

5390
/// ์›๋ณธ ์‚ฌ์šด๋“œ ์œ ํ˜• ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์—ด๊ฑฐํ˜•

โ€ŽRelaxOn/App/ViewModel/CustomSoundViewModel.swiftโ€Ž

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,21 +78,21 @@ final class CustomSoundViewModel: ObservableObject {
7878
}
7979

8080
/// sound์˜ ์žฌ์ƒ ๊ฐ„๊ฒฉ ์ €์žฅ
81-
@Published var interval: Float {
81+
@Published var interval: Float = 1.0 {
8282
didSet {
8383
audioEngineManager.audioVariation.interval = interval
8484
}
8585
}
8686

8787
/// sound์˜ ํ”ผ์น˜ ์ €์žฅ
88-
@Published var pitch = Float() {
88+
@Published var pitch: Float = 0 {
8989
didSet {
9090
audioEngineManager.audioVariation.pitch = pitch
9191
}
9292
}
9393

9494
/// sound์˜ ๋ณผ๋ฅจ ์ €์žฅ
95-
@Published var volume = Float() {
95+
@Published var volume: Float = 1.0 {
9696
didSet {
9797
audioEngineManager.audioVariation.volume = volume
9898
}
@@ -129,9 +129,6 @@ final class CustomSoundViewModel: ObservableObject {
129129
init(customSound: CustomSound? = nil, filter: AudioFilter = .none) {
130130
self.selectedSound = customSound
131131
self.filter = filter
132-
self.interval = 0.0
133-
self.pitch = 0.0
134-
self.volume = 0.0
135132
self.lastSound = userDefaults.lastPlayedSound
136133
self.filters = []
137134
}
@@ -142,12 +139,16 @@ final class CustomSoundViewModel: ObservableObject {
142139
extension CustomSoundViewModel {
143140

144141
func play<T: Playable>(with sound: T) {
145-
isPlaying.toggle()
142+
if !isPlaying {
143+
isPlaying = true
144+
}
146145
audioEngineManager.play(with: sound)
147146
}
148147

149148
func stopSound() {
150-
isPlaying.toggle()
149+
if isPlaying {
150+
isPlaying = false
151+
}
151152
audioEngineManager.stop()
152153
}
153154

โ€ŽRelaxOn/App/Views/Listen/ListenListView.swiftโ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ struct ListenListView: View {
4444
withAnimation {
4545
isShowingPlayer = true
4646
}
47+
if viewModel.isPlaying {
48+
viewModel.stopSound()
49+
viewModel.play(with: file)
50+
} else {
51+
viewModel.play(with: file)
52+
}
4753
}
4854
}
4955
.onDelete { indexSet in

0 commit comments

Comments
ย (0)