parent
b9fde1b0c2
commit
d58966bfbd
@ -0,0 +1,33 @@ |
||||
<?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="sound" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> |
||||
</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="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="AbstractTimer" 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> |
||||
@ -0,0 +1,52 @@ |
||||
// |
||||
// TextToSpeechRecorder.swift |
||||
// LeCountdown |
||||
// |
||||
// Created by Laurent Morvillier on 07/02/2023. |
||||
// |
||||
|
||||
import Foundation |
||||
import AVFoundation |
||||
|
||||
struct TextToSpeechFile: Codable { |
||||
var speech: String |
||||
var fileName: String |
||||
} |
||||
|
||||
class TextToSpeechRecorder { |
||||
|
||||
static func record(speech: String, handler: @escaping (Result<TextToSpeechFile, Error>) -> Void) { |
||||
let synthesizer = AVSpeechSynthesizer() |
||||
let utterance = AVSpeechUtterance(string: speech) |
||||
|
||||
utterance.voice = AVSpeechSynthesisVoice() |
||||
var output: AVAudioFile? |
||||
|
||||
synthesizer.write(utterance) { buffer in |
||||
guard let pcmBuffer = buffer as? AVAudioPCMBuffer else { |
||||
fatalError("unknown buffer type: \(buffer)") |
||||
} |
||||
let fileName = "\(UUID().uuidString).caf" |
||||
if pcmBuffer.frameLength == 0 { |
||||
handler(.success(TextToSpeechFile(speech: speech, fileName: fileName))) |
||||
// done |
||||
} else { |
||||
// append buffer to file |
||||
do { |
||||
if output == nil { |
||||
|
||||
output = try AVAudioFile( |
||||
forWriting: URL(fileURLWithPath: fileName), |
||||
settings: pcmBuffer.format.settings, |
||||
commonFormat: .pcmFormatInt16, |
||||
interleaved: false) |
||||
} |
||||
try output?.write(from: pcmBuffer) |
||||
} catch { |
||||
handler(.failure(error)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue