Create CustomSound, yet not activated

release
Laurent 3 years ago
parent 8c0bc4e585
commit 8baaa579e7
  1. 30
      LeCountdown.xcodeproj/project.pbxproj
  2. 3
      LeCountdown/Model/Generation/AbstractSoundTimer+CoreDataProperties.swift
  3. 15
      LeCountdown/Model/Generation/CustomSound+CoreDataClass.swift
  4. 26
      LeCountdown/Model/Generation/CustomSound+CoreDataProperties.swift
  5. 4
      LeCountdown/Model/Generation/Stopwatch+CoreDataProperties.swift
  6. 2
      LeCountdown/Model/LeCountdown.xcdatamodeld/.xccurrentversion
  7. 37
      LeCountdown/Model/LeCountdown.xcdatamodeld/LeCountdown.0.5.1.xcdatamodel/contents
  8. 9
      LeCountdown/Model/Model+Extensions.swift
  9. 21
      LeCountdown/Sound/Sound.swift
  10. 5
      LeCountdown/Utils/TextToSpeechRecorder.swift
  11. 64
      LeCountdown/Views/Components/SoundSelectionView.swift

@ -77,6 +77,14 @@
C4BA2AE62995AC3F00CB4FBA /* Loop_ToneSD_Boavista.wav in Resources */ = {isa = PBXBuildFile; fileRef = C4BA2AE52995AC3E00CB4FBA /* Loop_ToneSD_Boavista.wav */; };
C4BA2AE82995ACC200CB4FBA /* Clave_Loop_LLL.wav in Resources */ = {isa = PBXBuildFile; fileRef = C4BA2AE72995ACC200CB4FBA /* Clave_Loop_LLL.wav */; };
C4BA2AEA2995AD1C00CB4FBA /* SEM_Synths_Loop4_Nothing_Like_You.wav in Resources */ = {isa = PBXBuildFile; fileRef = C4BA2AE92995AD1C00CB4FBA /* SEM_Synths_Loop4_Nothing_Like_You.wav */; };
C4BA2AF02996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEC2996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift */; };
C4BA2AF12996A11900CB4FBA /* CustomSound+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AED2996A11900CB4FBA /* CustomSound+CoreDataClass.swift */; };
C4BA2AF22996A11900CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEE2996A11900CB4FBA /* CustomSound+CoreDataProperties.swift */; };
C4BA2AF32996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEF2996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift */; };
C4BA2AF62996A4EF00CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEE2996A11900CB4FBA /* CustomSound+CoreDataProperties.swift */; };
C4BA2AF72996A4EF00CB4FBA /* CustomSound+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AED2996A11900CB4FBA /* CustomSound+CoreDataClass.swift */; };
C4BA2AF82996A4F000CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEE2996A11900CB4FBA /* CustomSound+CoreDataProperties.swift */; };
C4BA2AF92996A4F000CB4FBA /* CustomSound+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AED2996A11900CB4FBA /* CustomSound+CoreDataClass.swift */; };
C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; };
C4F8B15729891271005C86A5 /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; };
C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C4F8B15829891528005C86A5 /* forest_stream.mp3 */; };
@ -251,6 +259,11 @@
C4BA2AE52995AC3E00CB4FBA /* Loop_ToneSD_Boavista.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = Loop_ToneSD_Boavista.wav; sourceTree = "<group>"; };
C4BA2AE72995ACC200CB4FBA /* Clave_Loop_LLL.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = Clave_Loop_LLL.wav; sourceTree = "<group>"; };
C4BA2AE92995AD1C00CB4FBA /* SEM_Synths_Loop4_Nothing_Like_You.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = SEM_Synths_Loop4_Nothing_Like_You.wav; sourceTree = "<group>"; };
C4BA2AEB2996A09600CB4FBA /* LeCountdown.0.5.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.5.1.xcdatamodel; sourceTree = "<group>"; };
C4BA2AEC2996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Stopwatch+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4BA2AED2996A11900CB4FBA /* CustomSound+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CustomSound+CoreDataClass.swift"; sourceTree = "<group>"; };
C4BA2AEE2996A11900CB4FBA /* CustomSound+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CustomSound+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4BA2AEF2996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AbstractSoundTimer+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = "<group>"; };
C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = "<group>"; };
C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = "<group>"; };
@ -518,7 +531,7 @@
isa = PBXGroup;
children = (
C4F8B1A5298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift */,
C4F8B1A6298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift */,
C4BA2AEF2996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift */,
C4F8B176298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift */,
C4F8B177298AC234005C86A5 /* AbstractTimer+CoreDataProperties.swift */,
C4F8B178298AC234005C86A5 /* Activity+CoreDataClass.swift */,
@ -527,9 +540,13 @@
C4F8B1AA298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift */,
C4F8B16C298AC234005C86A5 /* Countdown+CoreDataClass.swift */,
C4F8B1A9298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift */,
C4BA2AED2996A11900CB4FBA /* CustomSound+CoreDataClass.swift */,
C4BA2AEE2996A11900CB4FBA /* CustomSound+CoreDataProperties.swift */,
C4F8B16E298AC234005C86A5 /* Record+CoreDataClass.swift */,
C4F8B16F298AC234005C86A5 /* Record+CoreDataProperties.swift */,
C4F8B174298AC234005C86A5 /* Stopwatch+CoreDataClass.swift */,
C4BA2AEC2996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift */,
C4F8B1A6298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift */,
C4F8B175298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift */,
);
path = Generation;
@ -801,7 +818,9 @@
C4F8B1B8298AC81D005C86A5 /* CountdownDialView.swift in Sources */,
C445FA922987CC8A0054D761 /* Sound.swift in Sources */,
C4F8B1D0298BF2E2005C86A5 /* DialView.swift in Sources */,
C4BA2AF32996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C4742B59298411E800D5D950 /* CountdownFormView.swift in Sources */,
C4BA2AF12996A11900CB4FBA /* CustomSound+CoreDataClass.swift in Sources */,
C4060DC2297AE73B003FAB80 /* ContentView.swift in Sources */,
C438C7C12980228B00BF3EF9 /* CountdownScheduler.swift in Sources */,
C498E5A3298D720600E90DE0 /* TestView.swift in Sources */,
@ -814,7 +833,9 @@
C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */,
C4F8B182298AC234005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */,
C4BA2ADB299549BC00CB4FBA /* TimerModel.swift in Sources */,
C4BA2AF02996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4F8B16B298AA240005C86A5 /* NewStopwatchView.swift in Sources */,
C4BA2AF22996A11900CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */,
C4060DF5297AE9A7003FAB80 /* TimeInterval+Extensions.swift in Sources */,
C4F8B166298A9ABB005C86A5 /* SoundImageFormView.swift in Sources */,
C4F8B17D298AC234005C86A5 /* Record+CoreDataProperties.swift in Sources */,
@ -866,6 +887,7 @@
C4F8B18B298AC288005C86A5 /* Record+CoreDataClass.swift in Sources */,
C4F8B195298AC288005C86A5 /* Activity+CoreDataClass.swift in Sources */,
C4F8B193298AC288005C86A5 /* Countdown+CoreDataClass.swift in Sources */,
C4BA2AF72996A4EF00CB4FBA /* CustomSound+CoreDataClass.swift in Sources */,
C4F8B1AD298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */,
C445FA87298448730054D761 /* CoolPic.swift in Sources */,
C438C8172982BE9C00BF3EF9 /* Model+Extensions.swift in Sources */,
@ -882,6 +904,7 @@
C4F8B18C298AC288005C86A5 /* Alarm+CoreDataClass.swift in Sources */,
C438C8192982BFDB00BF3EF9 /* NSManagedContext+Extensions.swift in Sources */,
C438C7DA2981216200BF3EF9 /* LaunchWidget.swift in Sources */,
C4BA2AF62996A4EF00CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */,
C4F8B192298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */,
C4F8B18E298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */,
C4F8B1AE298AC451005C86A5 /* Alarm+CoreDataProperties.swift in Sources */,
@ -897,6 +920,7 @@
C4F8B1C6298ACC1F005C86A5 /* SoundPlayer.swift in Sources */,
C4F8B1A2298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */,
C4F8B1B2298AC451005C86A5 /* Alarm+CoreDataProperties.swift in Sources */,
C4BA2AF92996A4F000CB4FBA /* CustomSound+CoreDataClass.swift in Sources */,
C4F8B1A1298AC288005C86A5 /* Countdown+CoreDataClass.swift in Sources */,
C438C7F529812BB200BF3EF9 /* IntentHandler.swift in Sources */,
C4F8B19C298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */,
@ -912,6 +936,7 @@
C4F8B1A0298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */,
C438C81A2982BFF100BF3EF9 /* NSManagedContext+Extensions.swift in Sources */,
C4F8B19D298AC288005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */,
C4BA2AF82996A4F000CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */,
C4F8B19E298AC288005C86A5 /* Stopwatch+CoreDataProperties.swift in Sources */,
C4F8B199298AC288005C86A5 /* Record+CoreDataClass.swift in Sources */,
C438C8012981327600BF3EF9 /* Persistence.swift in Sources */,
@ -1373,6 +1398,7 @@
C4060DCA297AE73D003FAB80 /* LeCountdown.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
C4BA2AEB2996A09600CB4FBA /* LeCountdown.0.5.1.xcdatamodel */,
C4BA2AD72993F7D200CB4FBA /* LeCountdown.0.5.xcdatamodel */,
C40FDB672993D5E80042A390 /* LeCountdown.0.4.xcdatamodel */,
C4F8B160298A90E8005C86A5 /* LeCountdown.0.3.xcdatamodel */,
@ -1380,7 +1406,7 @@
C418A14F298428CB00C22230 /* LeCountdown.0.1.xcdatamodel */,
C4060DCB297AE73D003FAB80 /* LeCountdown.xcdatamodel */,
);
currentVersion = C4BA2AD72993F7D200CB4FBA /* LeCountdown.0.5.xcdatamodel */;
currentVersion = C4BA2AEB2996A09600CB4FBA /* LeCountdown.0.5.1.xcdatamodel */;
path = LeCountdown.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;

@ -2,7 +2,7 @@
// AbstractSoundTimer+CoreDataProperties.swift
// LeCountdown
//
// Created by Laurent Morvillier on 08/02/2023.
// Created by Laurent Morvillier on 10/02/2023.
//
//
@ -17,7 +17,6 @@ extension AbstractSoundTimer {
}
@NSManaged public var repeatCount: Int16
@NSManaged public var playlistList: String?
@NSManaged public var soundList: String?
}

@ -0,0 +1,15 @@
//
// CustomSound+CoreDataClass.swift
// LeCountdown
//
// Created by Laurent Morvillier on 10/02/2023.
//
//
import Foundation
import CoreData
@objc(CustomSound)
public class CustomSound: NSManagedObject {
}

@ -0,0 +1,26 @@
//
// CustomSound+CoreDataProperties.swift
// LeCountdown
//
// Created by Laurent Morvillier on 10/02/2023.
//
//
import Foundation
import CoreData
extension CustomSound {
@nonobjc public class func fetchRequest() -> NSFetchRequest<CustomSound> {
return NSFetchRequest<CustomSound>(entityName: "CustomSound")
}
@NSManaged public var text: String?
@NSManaged public var file: String?
}
extension CustomSound : Identifiable {
}

@ -2,7 +2,7 @@
// Stopwatch+CoreDataProperties.swift
// LeCountdown
//
// Created by Laurent Morvillier on 01/02/2023.
// Created by Laurent Morvillier on 10/02/2023.
//
//
@ -16,8 +16,8 @@ extension Stopwatch {
return NSFetchRequest<Stopwatch>(entityName: "Stopwatch")
}
@NSManaged public var start: Date?
@NSManaged public var end: Date?
@NSManaged public var sound: Int16
@NSManaged public var start: Date?
}

@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>LeCountdown.0.5.xcdatamodel</string>
<string>LeCountdown.0.5.1.xcdatamodel</string>
</dict>
</plist>

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22A400" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="AbstractSoundTimer" representedClassName="AbstractSoundTimer" isAbstract="YES" parentEntity="AbstractTimer" syncable="YES">
<attribute name="repeatCount" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="soundList" optional="YES" attributeType="String"/>
</entity>
<entity name="AbstractTimer" representedClassName="AbstractTimer" isAbstract="YES" syncable="YES">
<attribute name="image" optional="YES" attributeType="String"/>
<attribute name="order" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="timers" inverseEntity="Activity"/>
</entity>
<entity name="Activity" representedClassName="Activity" syncable="YES">
<attribute name="name" attributeType="String" defaultValueString=""/>
<relationship name="records" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Record" inverseName="activity" inverseEntity="Record"/>
<relationship name="timers" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AbstractTimer" inverseName="activity" inverseEntity="AbstractTimer"/>
</entity>
<entity name="Alarm" representedClassName="Alarm" parentEntity="AbstractSoundTimer" syncable="YES">
<attribute name="fireDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
</entity>
<entity name="Countdown" representedClassName="Countdown" parentEntity="AbstractSoundTimer" syncable="YES">
<attribute name="duration" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
</entity>
<entity name="CustomSound" representedClassName="CustomSound" syncable="YES">
<attribute name="file" optional="YES" attributeType="String"/>
<attribute name="text" optional="YES" attributeType="String"/>
</entity>
<entity name="Record" representedClassName="Record" syncable="YES">
<attribute name="end" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/>
<attribute name="start" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/>
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="records" inverseEntity="Activity"/>
</entity>
<entity name="Stopwatch" representedClassName="Stopwatch" parentEntity="AbstractSoundTimer" syncable="YES">
<attribute name="end" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="sound" optional="YES" attributeType="Integer 16" usesScalarValueType="YES"/>
<attribute name="start" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
</entity>
</model>

@ -145,6 +145,15 @@ extension Activity {
}
extension CustomSound : Localized {
var localizedString: String {
return self.text ?? ""
}
}
// MARK: - Storage convenience
fileprivate let separator = "|"

@ -32,21 +32,15 @@ enum Playlist: Int, CaseIterable, Identifiable, Localized {
var id: Int { return self.rawValue }
case nature = 1
case custom
case nature
case fun
case stephanBodzin
// var sounds: [Sound] {
// switch self {
// case .nature:
// return Sound.allCases
// case .fun:
// return Sound.allCases
// case .stephanBodzin:
// return Sound.allCases
// }
// }
//
static var selectable: [Playlist] {
return Playlist.allCases.filter { $0 != .custom }
}
var localizedString: String {
switch self {
case .nature:
@ -55,8 +49,11 @@ enum Playlist: Int, CaseIterable, Identifiable, Localized {
return NSLocalizedString("Fun", comment: "")
case .stephanBodzin:
return "Stephan Bodzin"
case .custom:
return NSLocalizedString("Custom", comment: "")
}
}
}
// Sound id are stored thus case order should not be changed

@ -18,8 +18,11 @@ class TextToSpeechRecorder {
static func record(speech: String, handler: @escaping (Result<TextToSpeechFile, Error>) -> Void) {
let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: speech)
utterance.voice = AVSpeechSynthesisVoice(language: "fr-FR")
synthesizer.speak(utterance)
return
utterance.voice = AVSpeechSynthesisVoice()
var output: AVAudioFile?
synthesizer.write(utterance) { buffer in

@ -10,18 +10,76 @@ import SwiftUI
struct PlaylistsView: View {
@EnvironmentObject var model: TimerModel
// @Environment(\.managedObjectContext) private var viewContext
// @FetchRequest(
// sortDescriptors: [NSSortDescriptor(keyPath: \CustomSound.text, ascending: true)],
// animation: .default)
// private var customSounds: FetchedResults<CustomSound>
//
// @State private var showNewSound: Bool = false
// @State private var name = ""
var body: some View {
Form {
ForEach(Playlist.allCases) { playlist in
// if customSounds.count > 0 {
//
// Section(Playlist.custom.localizedString) {
//
// ForEach(self.customSounds) { customSound in
// ToggleRow(item: customSound, selected: .constant(false)) { selected in
// // todo
// }
// }
// }
//
// }
ForEach(Playlist.selectable) { playlist in
PlaylistSectionView(playlist: playlist)
.environmentObject(self.model)
}
}.navigationTitle("Sounds")
}
.navigationTitle("Sounds")
// .alert("", isPresented: $showNewSound) {
// TextField("Enter your name", text: $name)
// Button("OK", action: _submit)
// } message: {
// Text("Xcode will print whatever you type.")
// }
// .toolbar {
// ToolbarItem(placement: .navigationBarTrailing) {
// Button {
// self.showNewSound = true
// } label: {
// Image(systemName: "plus")
// }
// }
// }
}
// fileprivate func _submit() {
// print("You entered \(name)")
// TextToSpeechRecorder.record(speech: self.name) { result in
// switch result {
// case .success(let file):
//
// let cs = CustomSound(context: viewContext)
// cs.text = self.name
// cs.file = file.fileName
//
// try! viewContext.save() // TODO
//
// case .failure(let failure):
// print("error = \(failure)")
// break
// }
// }
// }
}
struct PlaylistSectionView: View {

Loading…
Cancel
Save