summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicoleYarroch <nicole@livio.io>2018-04-20 11:12:00 -0400
committerNicoleYarroch <nicole@livio.io>2018-04-20 11:12:00 -0400
commita925ca7c4b1cbcb48dbc769759403f147905ac30 (patch)
tree30e72f85a92c005e1cca2721fd8501d97eb97673
parenta007b1c2e6ea7585fde6b5733df43d70cad9f1c8 (diff)
downloadsdl_ios-a925ca7c4b1cbcb48dbc769759403f147905ac30.tar.gz
Added more documentation to the Swift example app
Signed-off-by: NicoleYarroch <nicole@livio.io>
-rw-r--r--SmartDeviceLink_Example/AlertManager.swift15
-rw-r--r--SmartDeviceLink_Example/AudioManager.swift181
-rw-r--r--SmartDeviceLink_Example/ButtonManager.swift7
-rw-r--r--SmartDeviceLink_Example/ProxyManager.swift17
-rw-r--r--SmartDeviceLink_Example/VehicleDataManager.swift7
5 files changed, 68 insertions, 159 deletions
diff --git a/SmartDeviceLink_Example/AlertManager.swift b/SmartDeviceLink_Example/AlertManager.swift
index a5e43c2b2..b4ecdad7d 100644
--- a/SmartDeviceLink_Example/AlertManager.swift
+++ b/SmartDeviceLink_Example/AlertManager.swift
@@ -10,20 +10,23 @@ import Foundation
import SmartDeviceLink
class AlertManager {
+ private class var okSoftButton: SDLSoftButton {
+ return SDLSoftButton(type: .text, text: "OK", image: nil, highlighted: true, buttonId: 1, systemAction: nil, handler: nil)
+ }
+
/// Creates an alert with a single line of text
///
/// - Parameter message: The message to display in the alert
/// - Returns: An SDLAlert object
- class func alertWithMessage(_ message: String) -> SDLAlert {
- return SDLAlert(alertText1: message, alertText2: nil, alertText3: nil)
+ class func alertWithMessage(_ textField1: String, textField2: String? = nil) -> SDLAlert {
+ return SDLAlert(alertText1: textField1, alertText2: nil, alertText3: nil)
}
- /// Creates an alert with up to two lines of text and a close alert button
+ /// Creates an alert with up to two lines of text and a close button that will dismiss the alert when tapped
///
/// - Parameter message: The message to display in the alert
/// - Returns: An SDLAlert object
- class func alertWithMessageAndCloseButton(_ message: String) -> SDLAlert {
- let okSoftButton = SDLSoftButton(type: .text, text: "OK", image: nil, highlighted: true, buttonId: 1, systemAction: nil, handler: nil)
- return SDLAlert(alertText1: message, alertText2: nil, alertText3: nil, duration: 5000, softButtons: [okSoftButton])
+ class func alertWithMessageAndCloseButton(_ textField1: String, textField2: String? = nil) -> SDLAlert {
+ return SDLAlert(alertText1: textField1, alertText2: textField2, alertText3: nil, duration: 5000, softButtons: [AlertManager.okSoftButton])
}
}
diff --git a/SmartDeviceLink_Example/AudioManager.swift b/SmartDeviceLink_Example/AudioManager.swift
index a0b227163..a7dcae75c 100644
--- a/SmartDeviceLink_Example/AudioManager.swift
+++ b/SmartDeviceLink_Example/AudioManager.swift
@@ -11,42 +11,42 @@ import AVFoundation
import SmartDeviceLink
import SmartDeviceLinkSwift
-private enum SearchManagerState {
+typealias audioRecordingHandler = ((AudioRecordingState) -> Void)
+
+enum AudioRecordingState {
case listening, notListening
}
class AudioManager: NSObject {
fileprivate let sdlManager: SDLManager!
- fileprivate var audioData = Data()
- fileprivate var audioRecordingState: SearchManagerState
-
- fileprivate var floorAudioDb: Float?
- fileprivate var numberOfSilentPasses = 0;
+ fileprivate var audioData: Data?
+ fileprivate var audioRecordingState: AudioRecordingState
init(sdlManager: SDLManager) {
self.sdlManager = sdlManager
+ audioData = Data()
audioRecordingState = .notListening
super.init()
- NotificationCenter.default.addObserver(self, selector: #selector(audioPassThruDataReceived(notification:)), name: .SDLDidReceiveAudioPassThru, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(audioPassThruEnded(response:)), name: SDLDidReceivePerformAudioPassThruResponse, object: nil)
}
- func stop() {
- stopRecording()
+ func stopManager() {
+ audioRecordingState = .notListening
+ audioData = Data()
}
+ /// Starts an audio recording using the in-car microphone. During the recording, a pop-up will let the user know that they are being recorded. The pop-up is only dismissed when the recording stops.
func startRecording() {
guard audioRecordingState == .notListening else { return }
- audioData = Data()
- let audioPassThruRequest = SDLPerformAudioPassThru(initialPrompt: nil, audioPassThruDisplayText1: "Listening", audioPassThruDisplayText2: "Say something...", samplingRate: .rate16KHZ, bitsPerSample: .sample16Bit, audioType: .PCM, maxDuration: 5000, muteAudio: true)
+ let recordingDurationMilliseconds: UInt32 = 5000
+ let audioPassThruRequest = SDLPerformAudioPassThru(initialPrompt: "Starting sound recording", audioPassThruDisplayText1: "Say Something", audioPassThruDisplayText2: "Recording for \(recordingDurationMilliseconds / 1000) seconds", samplingRate: .rate16KHZ, bitsPerSample: .sample16Bit, audioType: .PCM, maxDuration: recordingDurationMilliseconds, muteAudio: true, audioDataHandler: audioDataHandler)
- sdlManager.send(request: audioPassThruRequest) { (_, response, error) in
- SDLLog.d("Audio Pass Thru request sent \(response?.resultCode == .success ? "successfully" : "unsuccessfully"). Error: \(error != nil ? error!.localizedDescription : "no error")")
- }
+ sdlManager.send(request: audioPassThruRequest)
}
+ /// Manually stops an on-going audio recording.
func stopRecording() {
guard audioRecordingState == .listening else { return }
audioRecordingState = .notListening
@@ -60,51 +60,53 @@ class AudioManager: NSObject {
// MARK: - Audio Pass Thru Notifications
extension AudioManager {
- /// Called when an audio chunk is recorded.
- ///
- /// - Parameter notification: A SDLRPCNotificationNotification notification
- @objc func audioPassThruDataReceived(notification: SDLRPCNotificationNotification) {
- guard let data = notification.notification.bulkData else {
- return
+ /// SDL streams the audio data as it is collected.
+ fileprivate var audioDataHandler: SDLAudioPassThruHandler? {
+ return { [unowned self] data in
+ guard data != nil else { return }
+ if self.audioRecordingState == .notListening {
+ self.audioData = Data()
+ self.audioRecordingState = .listening
+ }
+ self.audioData!.append(data!)
}
-
- // Current audio chunk
- // let _ = AudioManager.convertDataToPCMFormattedAudio(data)
- convertDataToPCMFormattedAudio(data)
-
- // Save the sound chunk
- audioData.append(data)
}
- /// Called after a `SDLEndAudioPassThru` request is sent or when a `SDLPerformAudioPassThru` request times out
+ /// Called when `PerformAudioPassThru` request times out or when a `EndAudioPassThru` request is sent
///
/// - Parameter response: A SDLRPCNotificationNotification notification
@objc func audioPassThruEnded(response: SDLRPCResponseNotification) {
- guard response.response.success.boolValue == true else {
- return
- }
+ guard audioRecordingState == .listening else { return }
+ audioRecordingState = .notListening
- // `audioData` contains the complete audio recording for the pass thru. SDL does not provide speech recognition, however the iOS Speech API or another third party library can be used for speech reconition.
- playRecording(audioData)
+ switch response.response.resultCode {
+ case .success: // The `PerformAudioPassThru` timed out or the "Done" button was pressed in the pop-up.
+ if audioData == nil {
+ sdlManager.send(AlertManager.alertWithMessageAndCloseButton("No audio recorded"))
+ } else {
+ // `audioData` contains the complete audio recording for the pass thru. SDL does not provide speech recognition, however the iOS Speech API or another third party library can be used for speech reconition.
+ let pcmBuffer = convertDataToPCMFormattedAudio(audioData!)
+ sdlManager.send(AlertManager.alertWithMessageAndCloseButton("Audio recorded!", textField2: "\(pcmBuffer)"))
+ }
+ case .aborted: // The "Cancel" button was pressed in the pop-up. Ignore this audio pass thru.
+ audioData = Data()
+ sdlManager.send(AlertManager.alertWithMessageAndCloseButton("Recording cancelled"))
+ default:
+ sdlManager.send(AlertManager.alertWithMessageAndCloseButton("Recording unsuccessful", textField2: "\(response.response.resultCode.rawValue.rawValue)"))
+ }
}
}
-// MARK: - Audio Pass Thru
+// MARK: - Audio Data Conversion
private extension AudioManager {
- func playRecording(_ data: Data) {
- var recording: AVAudioPlayer?
- do {
- recording = try AVAudioPlayer(data: data)
- recording?.play()
- } catch {
- SDLLog.e("Failed to play recording")
- }
- }
-
- func convertDataToPCMFormattedAudio(_ data: Data) {
- // Currently, SDL only supports Sampling Rates of 16 khz and Bit Rates of 16 bit.
+ /// Converts the audio data to PCM formatted audio that can be passed to iOS SFSpeech Framework, if desired. When doing the converstion, the audio format and sample rate should match those set in the `SDLPerformAudioPassThru`.
+ ///
+ /// - Parameter data: The audio data
+ /// - Returns: A AVAudioPCMBuffer object
+ func convertDataToPCMFormattedAudio(_ data: Data) -> AVAudioPCMBuffer {
+ //
let audioFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 16000, channels: 1, interleaved: false)
let numFrames = UInt32(data.count) / (audioFormat.streamDescription.pointee.mBytesPerFrame)
let buffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: numFrames)
@@ -112,92 +114,9 @@ private extension AudioManager {
let bufferChannels = buffer.int16ChannelData!
let bufferDataCount = data.copyBytes(to: UnsafeMutableBufferPointer(start: bufferChannels[0], count: data.count))
- // print("Sound chunk has \(bufferDataCount) bytes in \(buffer)")
- // print("PCM: frame capacity: \(buffer.frameCapacity)")
- // print("PCM: frame length: \(buffer.frameLength)")
- //
- let elements16 = UnsafeBufferPointer(start: buffer.int16ChannelData?[0], count:data.count)
- var samples = [Int16]()
- for i in stride(from: 0, to: buffer.frameLength, by: 1) {
- samples.append(elements16[Int(i)])
- let decibles = Float(AudioManager.computeDecibels(samples[Int(i)]))
- print("decibles: \(decibles)")
-
- guard let floorAudioDb = floorAudioDb, decibles != 0 else {
- self.floorAudioDb = floorf(decibles) + 5
- return
- }
-
- if decibles > floorAudioDb {
- audioRecordingState = .listening
- numberOfSilentPasses = 0
- print("not silent")
- } else {
- numberOfSilentPasses += 1
- print("silent")
- }
-
- if numberOfSilentPasses == 30 {
- print("silent chunk")
- }
- }
- }
-
- // class func checkAmplitude(_ data: Data) {
- // let pcmAudio = convertDataToPCMFormattedAudio(data)
- //
- // let elements16 = UnsafeBufferPointer(start: pcmAudio.int16ChannelData?[0], count:data.count)
- // var samples = [Int16]()
- // for i in stride(from: 0, to: pcmAudio.frameLength, by: 1) {
- // samples.append(elements16[Int(i)])
- // let decibles = computeDecibels(samples[Int(i)])
- // print("sample: \(samples[Int(i)]), decibles: \(decibles)")
- // }
- // }
-
- func computeSilentPasses(_ silentPassesCount: Int) {
- if audioRecordingState == .listening && numberOfSilentPasses == 30 {
- audioRecordingState = .notListening
- numberOfSilentPasses = 0
- stopRecording()
- }
- }
-
- func computeSilentPasses(_ currentDb: Float) -> Int {
- guard let floorAudioDb = floorAudioDb else {
- self.floorAudioDb = floorf(currentDb) + 5;
- return 0
- }
-
- if currentDb > floorAudioDb {
- audioRecordingState = .listening
- numberOfSilentPasses = 0
- } else {
- numberOfSilentPasses += 1
- }
-
- return numberOfSilentPasses
- }
+ SDLLog.d("Audio data has \(bufferDataCount) bytes in \(buffer)")
- // class func computeAmplitude(_ data: Data) {
- // let pcmBuffer = convertDataToPCMFormattedAudio(data)
- //
- // let channelData = UnsafeBufferPointer(start: pcmBuffer.int16ChannelData?[0], count:data.count)
- // var samples = [Int16]()
- // for i in stride(from: 0, to: pcmBuffer.frameLength, by: 1) {
- // samples.append(channelData[Int(i)])
- // // let decibels = computeDecibels(samples[Int(i)])
- // print("sample: \(samples[Int(i)])")
- // }
- // }
-
- // https://stackoverflow.com/questions/2445756/how-can-i-calculate-audio-db-level
- class func computeDecibels(_ sample: Int16) -> Double {
- let amplitude = Double(sample) / Double(Int16.max)
- guard amplitude > 0 else { return 0 }
-
- let decibels = 20 * log10(amplitude)
- return decibels
+ return buffer
}
}
diff --git a/SmartDeviceLink_Example/ButtonManager.swift b/SmartDeviceLink_Example/ButtonManager.swift
index b69db0e73..7994e95b3 100644
--- a/SmartDeviceLink_Example/ButtonManager.swift
+++ b/SmartDeviceLink_Example/ButtonManager.swift
@@ -41,11 +41,6 @@ class ButtonManager: NSObject {
}
}
- /// Custom init
- ///
- /// - Parameters:
- /// - sdlManager: The SDL Manager
- /// - updateScreenHandler: handler for refreshing the current SDL UI
init(sdlManager: SDLManager, updateScreenHandler: refreshUIHandler? = nil) {
self.sdlManager = sdlManager
self.refreshUIHandler = updateScreenHandler
@@ -55,7 +50,7 @@ class ButtonManager: NSObject {
super.init()
}
- func stop() {
+ func stopManager() {
textEnabled = true
imagesEnabled = true
toggleEnabled = true
diff --git a/SmartDeviceLink_Example/ProxyManager.swift b/SmartDeviceLink_Example/ProxyManager.swift
index 85ee14889..70870f7e0 100644
--- a/SmartDeviceLink_Example/ProxyManager.swift
+++ b/SmartDeviceLink_Example/ProxyManager.swift
@@ -13,7 +13,6 @@ class ProxyManager: NSObject {
fileprivate var sdlManager: SDLManager!
fileprivate var buttonManager: ButtonManager!
fileprivate var vehicleDataManager: VehicleDataManager!
- fileprivate var audioManager: AudioManager!
fileprivate var firstHMILevelState: SDLHMILevelFirstState
weak var delegate: ProxyManagerDelegate?
@@ -78,7 +77,7 @@ private extension ProxyManager {
return SDLConfiguration(lifecycle: lifecycleConfiguration, lockScreen: lockScreenConfiguration, logging: logConfiguration())
}
- /// Sets the type of SDL debug logs that are visible and where to port the logs. There are 4 levels of logs, verbose, debug, warning and error, which verbose printing all SDL logs and error printing only the error logs. Adding SDLLogTargetFile to the targest will log to a text file on the iOS device. This file can be accessed via: iTunes > Your Device Name > File Sharing > Your App Name. Make sure `UIFileSharingEnabled` has been added to the application's info.plist and is set to `true`.
+ /// Sets the type of SDL debug logs that are visible and where to port the logs. There are 4 levels of log filtering, verbose, debug, warning and error. Verbose prints all SDL logs; error prints only the error logs. Adding SDLLogTargetFile to the targest will log to a text file on the iOS device. This file can be accessed via: iTunes > Your Device Name > File Sharing > Your App Name. Make sure `UIFileSharingEnabled` has been added to the application's info.plist and is set to `true`.
///
/// - Returns: A SDLLogConfiguration object
class func logConfiguration() -> SDLLogConfiguration {
@@ -86,11 +85,11 @@ private extension ProxyManager {
let exampleLogFileModule = SDLLogFileModule(name: "SDL Example", files: ["ProxyManager"])
logConfig.modules.insert(exampleLogFileModule)
_ = logConfig.targets.insert(SDLLogTargetFile()) // Logs to file
- logConfig.globalLogLevel = .off // Filters the logs
+ logConfig.globalLogLevel = .debug // Filters the logs
return logConfig
}
- /// Searches for a connection to a SDL enabled accessory. When a connection has been established, the ready handler is called. Even though the app is connected to SDL Core, it does not mean that RPCs can be immediately sent to the accessory as there is no guarentee that SDL Core is ready to receive RPCs. Monitor the `SDLManagerDelegate`'s `hmiLevel(_:didChangeToLevel:)` to determine when to send RPCs.
+ /// Searches for a connection to a SDL enabled accessory. When a connection has been established, the ready handler is called. Even though the app is connected to SDL Core, it does not mean that RPCs can be immediately sent to the accessory as there is no guarentee that SDL Core is ready to receive RPCs. Monitor the `SDLManagerDelegate`'s `hmiLevel:didChangeToLevel:` to determine when to send RPCs.
func startManager() {
sdlManager.start(readyHandler: { [unowned self] (success, error) in
guard success else {
@@ -99,13 +98,10 @@ private extension ProxyManager {
return
}
- // A connection has been established between the app and a SDL enabled accessory
self.delegate?.didChangeProxyState(SDLProxyState.connected)
- // Do some setup
self.buttonManager = ButtonManager(sdlManager: self.sdlManager, updateScreenHandler: self.refreshUIHandler)
self.vehicleDataManager = VehicleDataManager(sdlManager: self.sdlManager, refreshUIHandler: self.refreshUIHandler)
- self.audioManager = AudioManager(sdlManager: self.sdlManager)
RPCPermissionsManager.setupPermissionsCallbacks(with: self.sdlManager)
@@ -121,9 +117,8 @@ extension ProxyManager: SDLManagerDelegate {
func managerDidDisconnect() {
delegate?.didChangeProxyState(SDLProxyState.stopped)
firstHMILevelState = .none
- buttonManager.stop()
- vehicleDataManager.stop()
- audioManager.stop()
+ buttonManager.stopManager()
+ vehicleDataManager.stopManager()
// If desired, automatically start searching for a new connection to Core
if ExampleAppShouldRestartSDLManagerOnDisconnect.boolValue {
@@ -203,6 +198,7 @@ private extension ProxyManager {
/// Handler for refreshing the UI
var refreshUIHandler: refreshUIHandler? {
return { [unowned self] () in
+ // self.audioManager.startRecording()
self.updateScreen()
}
}
@@ -221,6 +217,7 @@ private extension ProxyManager {
/// Update the UI's textfields, images and soft buttons
func updateScreen() {
guard sdlManager.hmiLevel == .full else { return }
+
let screenManager = sdlManager.screenManager
let isTextVisible = buttonManager.textEnabled
let areImagesVisible = buttonManager.imagesEnabled
diff --git a/SmartDeviceLink_Example/VehicleDataManager.swift b/SmartDeviceLink_Example/VehicleDataManager.swift
index 603e29156..f757dcb51 100644
--- a/SmartDeviceLink_Example/VehicleDataManager.swift
+++ b/SmartDeviceLink_Example/VehicleDataManager.swift
@@ -15,11 +15,6 @@ class VehicleDataManager: NSObject {
fileprivate var refreshUIHandler: refreshUIHandler?
public fileprivate(set) var vehicleOdometerData: String
- /// Custom init
- ///
- /// - Parameters:
- /// - sdlManager: The SDL Manager
- /// - refreshOdometerHandler: handler for refreshing the UI with new odometer data
init(sdlManager: SDLManager, refreshUIHandler: refreshUIHandler? = nil) {
self.sdlManager = sdlManager
self.refreshUIHandler = refreshUIHandler
@@ -30,7 +25,7 @@ class VehicleDataManager: NSObject {
NotificationCenter.default.addObserver(self, selector: #selector(vehicleDataNotification(_:)), name: .SDLDidReceiveVehicleData, object: nil)
}
- func stop() {
+ func stopManager() {
resetOdometer()
}
}