Skip to content
This repository has been archived by the owner on Oct 16, 2023. It is now read-only.

Add support for audio ops (Spectrogram & MFCC) in Coreml and add trained coreml model #103

Merged
merged 21 commits into from Apr 5, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
71fd2c0
Add CoreML Model + supporting code
timorohner Mar 11, 2019
f76bfd9
Fix linting
timorohner Mar 11, 2019
106c972
Extracted Machine Learning Math helper functions into seperate MathUt…
timorohner Mar 12, 2019
2d92f08
Added non Accelerate vDSP implementation for mfccDct Op
timorohner Mar 12, 2019
2481062
Added automatic detection for whether to use Accelerate DCT or slower…
timorohner Mar 13, 2019
8997e04
Fixing linting errors, adding better Exception handling
timorohner Mar 13, 2019
6bf2cdc
Enable passing crying baby detection results instead of a constant no…
timorohner Mar 15, 2019
f6ebdfa
Extract AudioMicrophoneRecord/CaptureServiceProtocol protocols to sep…
timorohner Mar 15, 2019
72b48b3
Removed redundant print calls and redundant empty lines
timorohner Mar 15, 2019
2fdfbe1
Rename microphoneRecord and microphoneCapture to microphoneRecordServ…
timorohner Mar 25, 2019
36d476c
Renamed capture and record variable to microphone{Capturer,Recorder} …
timorohner Mar 25, 2019
5e82a92
Update naming to reflect previous changes from AudioRecordService to …
timorohner Mar 25, 2019
34718cf
Changes addressing comments from CodeReview
timorohner Mar 25, 2019
a0b0204
Merge branch 'develop' into coreml
Apr 3, 2019
e461fee
Update tests to reflect changes in cry detection
akashivskyy Apr 3, 2019
ddeb454
Address minor code review suggestions
akashivskyy Apr 3, 2019
4522027
Update dependencies for Xcode 10.2
akashivskyy Apr 3, 2019
fbf872d
Revert upgrade to Xcode 10.2
akashivskyy Apr 3, 2019
39f4adc
Merge branch 'develop' into coreml
Apr 4, 2019
6d63886
Bump AudioKit and fix audio capturing
akashivskyy Apr 4, 2019
72fb7bd
Added Documentation
timorohner Apr 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
96 changes: 72 additions & 24 deletions Baby Monitor.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions Baby Monitor/Source Files/AppDependencies.swift
Expand Up @@ -15,14 +15,14 @@ final class AppDependencies {
private let bag = DisposeBag()
/// Service for cleaning too many crying events
private(set) lazy var memoryCleaner: MemoryCleanerProtocol = MemoryCleaner()
/// Service for recording audio
private(set) lazy var audioRecordService: AudioRecordServiceProtocol? = try? AudioRecordService(recorderFactory: AudioKitRecorderFactory.makeRecorderFactory)
/// Service for capturing/recording microphone audio
private(set) lazy var audioMicrophoneService: AudioMicrophoneServiceProtocol? = try? AudioMicrophoneService(microphoneFactory: AudioKitMicrophoneFactory.makeMicrophoneFactory)
/// Service for detecting baby's cry
private(set) lazy var cryingDetectionService: CryingDetectionServiceProtocol = CryingDetectionService(microphoneTracker: AKMicrophoneTracker())
private(set) lazy var cryingDetectionService: CryingDetectionServiceProtocol = CryingDetectionService(microphoneCapture: audioMicrophoneService)
/// Service that takes care of appropriate controling: crying detection, audio recording and saving these events to realm database
private(set) lazy var cryingEventService: CryingEventsServiceProtocol = CryingEventService(
cryingDetectionService: cryingDetectionService,
audioRecordService: audioRecordService,
microphoneRecord: audioMicrophoneService,
activityLogEventsRepository: databaseRepository,
storageService: storageServerService)

Expand Down
81 changes: 0 additions & 81 deletions Baby Monitor/Source Files/Services/Crying/AudioRecordService.swift

This file was deleted.

This file was deleted.

@@ -0,0 +1,119 @@
//
// AudioMicrophoneService.swift
// Baby Monitor
//

import Foundation
import AudioKit
import RxSwift
import RxCocoa

protocol ErrorProducable {
var errorObservable: Observable<Error> { get }
}

protocol AudioMicrophoneRecordServiceProtocol {
var directoryDocumentsSavableObservable: Observable<DirectoryDocumentsSavable> { get }
var isRecording: Bool { get }

func stopRecording()
func startRecording()
}

protocol AudioMicrophoneCaptureServiceProtocol {
var microphoneBufferReadableObservable: Observable<AVAudioPCMBuffer> { get }
var isCapturing: Bool { get }

func stopCapturing()
func startCapturing()
}

protocol AudioMicrophoneServiceProtocol: AudioMicrophoneRecordServiceProtocol, AudioMicrophoneCaptureServiceProtocol {}
akashivskyy marked this conversation as resolved.
Show resolved Hide resolved

final class AudioMicrophoneService: AudioMicrophoneServiceProtocol, ErrorProducable {

enum AudioError: Error {
timorohner marked this conversation as resolved.
Show resolved Hide resolved
case initializationFailure
case captureFailure
case recordFailure
case saveFailure
}

lazy var errorObservable = errorSubject.asObservable()
timorohner marked this conversation as resolved.
Show resolved Hide resolved
lazy var microphoneBufferReadableObservable = microphoneBufferReadableSubject.asObservable()
lazy var directoryDocumentsSavableObservable = directoryDocumentsSavableSubject.asObservable()

private(set) var isCapturing = false
private(set) var isRecording = false

private var capture: MicrophoneCaptureProtocol
private var record: MicrophoneRecordProtocol

private let errorSubject = PublishSubject<Error>()
private let microphoneBufferReadableSubject = PublishSubject<AVAudioPCMBuffer>()
private let directoryDocumentsSavableSubject = PublishSubject<DirectoryDocumentsSavable>()

private let disposeBag = DisposeBag()

init(microphoneFactory: () throws -> AudioKitMicrophoneProtocol?) throws {
guard let audioKitMicrophone = try microphoneFactory() else {
throw(AudioMicrophoneService.AudioError.initializationFailure)
}
capture = audioKitMicrophone.capture
record = audioKitMicrophone.record
rxSetup()
}

func stopCapturing() {
guard isCapturing else {
return
}
capture.stop()
isCapturing = false
}

func startCapturing() {
guard !isCapturing else {
return
}
do {
try capture.start()
} catch {
return
}
isCapturing = true
}

func stopRecording() {
guard isRecording else {
return
}
record.stop()
isRecording = false
guard let audioFile = record.audioFile else {
errorSubject.onNext(AudioError.recordFailure)
return
}
directoryDocumentsSavableSubject.onNext(audioFile)
}

func startRecording() {
guard !isRecording else {
return
}
do {
try record.reset()
try record.record()
isRecording = true
} catch {
errorSubject.onNext(AudioError.recordFailure)
}
}

private func rxSetup() {
capture.bufferReadable.subscribe(onNext: { [unowned self] bufferReadable in
self.microphoneBufferReadableSubject.onNext(bufferReadable)
}).disposed(by: disposeBag)
}

}
@@ -0,0 +1,72 @@
//
// CryingDetectionService.swift
// Baby Monitor
//

import Foundation
import CoreML
import AudioKit
import RxSwift
import RxCocoa

protocol CryingDetectionServiceProtocol: Any {

/// Observable that informs about detection of baby's cry
var cryingDetectionObservable: Observable<Bool> { get }

/// Starts crying detection
func startAnalysis()
/// Stops crying detection
func stopAnalysis()
}

final class CryingDetectionService: CryingDetectionServiceProtocol {

lazy var cryingDetectionObservable = cryingDetectionSubject.asObservable()

private let cryingDetectionSubject = PublishSubject<Bool>()

private let microphoneCapture: AudioMicrophoneCaptureServiceProtocol?
private let disposeBag = DisposeBag()
private let audioprocessingModel = audioprocessing()
private let crydetectionModel = crydetection()

init(microphoneCapture: AudioMicrophoneCaptureServiceProtocol?) {
self.microphoneCapture = microphoneCapture
rxSetup()
}

func startAnalysis() {
microphoneCapture?.startCapturing()
}

func stopAnalysis() {
microphoneCapture?.stopCapturing()
}

private func rxSetup() {
microphoneCapture?.microphoneBufferReadableObservable.subscribe(onNext: { [unowned self] bufferReadable in
do {
print("RUNNING ML MODEL")
timorohner marked this conversation as resolved.
Show resolved Hide resolved
let audioProcessingMultiArray = try MLMultiArray(dataPointer: bufferReadable.floatChannelData!.pointee,
shape: [264600],
dataType: .float32,
strides: [1])

let input = audioprocessingInput(raw_audio__0: audioProcessingMultiArray)
let pred = try self.audioprocessingModel.prediction(input: input)
let crydetectionMultiArray = try MLMultiArray(shape: [1, 1, 1, 598, 64], dataType: .float32)
crydetectionMultiArray.dataPointer.copyMemory(from: pred.Mfcc__0.dataPointer, byteCount: 38272 * 4)
let input1 = crydetectionInput(Mfcc__0: crydetectionMultiArray)
let pred2 = try self.crydetectionModel.prediction(input: input1)
let babyCryingDetected: Bool = pred2.labels_softmax__0[0].compare(pred2.labels_softmax__0[1]) == .orderedAscending
self.cryingDetectionSubject.onNext(babyCryingDetected)

print(pred2.labels_softmax__0)
timorohner marked this conversation as resolved.
Show resolved Hide resolved

timorohner marked this conversation as resolved.
Show resolved Hide resolved
} catch {
print("ERROR")
}
}).disposed(by: disposeBag)
}
}