From 8266c33579a59eb07142a18367bc3a42529e7bf3 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 28 Nov 2023 16:08:58 +0900 Subject: [PATCH 01/24] =?UTF-8?q?TextAgent=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20(#1110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - TextAgent 업데이트 - version 1.8 - TextInput event field에 service object 추가 - --- .../CapabilityAgents/Text/TextAgent.swift | 20 ++++++++++++++++--- .../Text/TextAgentProtocol.swift | 10 +++++++++- NuguClientKit/Sources/Client/NuguClient.swift | 8 ++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift index f7012c2d7..1c3d95884 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift @@ -26,7 +26,7 @@ import RxSwift public final class TextAgent: TextAgentProtocol { // CapabilityAgentable - public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .text, version: "1.7") + public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .text, version: "1.8") public weak var delegate: TextAgentDelegate? // Private @@ -119,6 +119,7 @@ extension TextAgent { token: String?, source: TextInputSource?, requestType: TextAgentRequestType, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String { sendFullContextEvent( @@ -126,7 +127,8 @@ extension TextAgent { text: text, token: token, source: source, - requestType: requestType + requestType: requestType, + service: service ), completion: completion ) @@ -138,6 +140,7 @@ extension TextAgent { token: String?, playServiceId: String?, source: TextInputSource?, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String { sendFullContextEvent( @@ -145,7 +148,8 @@ extension TextAgent { text: text, token: token, playServiceId: playServiceId, - source: source + source: source, + service: service ), completion: completion ) @@ -327,6 +331,7 @@ private extension TextAgent { token: String?, source: TextInputSource? = nil, requestType: TextAgentRequestType, + service: [String: AnyHashable]? = nil, referrerDialogRequestId: String? = nil ) -> Single { return Single<[String: AnyHashable]>.create { [weak self] single in @@ -356,6 +361,10 @@ private extension TextAgent { attributes["interactionControl"] = interactionControlDictionary } + if let service = service { + attributes["service"] = service + } + return attributes } @@ -377,6 +386,7 @@ private extension TextAgent { token: String?, playServiceId: String?, source: TextInputSource? = nil, + service: [String: AnyHashable]? = nil, referrerDialogRequestId: String? = nil ) -> Single { return Single<[String: AnyHashable]>.create { [weak self] single in @@ -403,6 +413,10 @@ private extension TextAgent { let interactionControlDictionary = try? JSONSerialization.jsonObject(with: interactionControlData, options: []) as? [String: AnyHashable] { attributes["interactionControl"] = interactionControlDictionary } + + if let service = service { + attributes["service"] = service + } single(.success(attributes)) } diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift index 25743827a..1a07dbfac 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift @@ -38,6 +38,7 @@ public protocol TextAgentProtocol: CapabilityAgentable { token: String?, source: TextInputSource?, requestType: TextAgentRequestType, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String @@ -51,6 +52,7 @@ public protocol TextAgentProtocol: CapabilityAgentable { token: String?, playServiceId: String?, source: TextInputSource?, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String } @@ -62,13 +64,15 @@ public extension TextAgentProtocol { text: String, token: String? = nil, source: TextInputSource? = nil, - requestType: TextAgentRequestType + requestType: TextAgentRequestType, + service: [String: AnyHashable]? = nil ) -> String { return requestTextInput( text: text, token: token, source: source, requestType: requestType, + service: service, completion: nil ) } @@ -78,6 +82,7 @@ public extension TextAgentProtocol { token: String? = nil, source: TextInputSource? = nil, requestType: TextAgentRequestType, + service: [String: AnyHashable]? = nil, completion: ((StreamDataState) -> Void)? ) -> String { return requestTextInput( @@ -85,6 +90,7 @@ public extension TextAgentProtocol { token: token, source: source, requestType: requestType, + service: service, completion: completion ) } @@ -94,6 +100,7 @@ public extension TextAgentProtocol { token: String? = nil, playServiceId: String? = nil, source: TextInputSource? = nil, + service: [String: AnyHashable]? = nil, completion: ((StreamDataState) -> Void)? = nil ) -> String { requestTextInput( @@ -101,6 +108,7 @@ public extension TextAgentProtocol { token: token, playServiceId: playServiceId, source: source, + service: service, completion: completion ) } diff --git a/NuguClientKit/Sources/Client/NuguClient.swift b/NuguClientKit/Sources/Client/NuguClient.swift index d566abbe7..b746e4717 100644 --- a/NuguClientKit/Sources/Client/NuguClient.swift +++ b/NuguClientKit/Sources/Client/NuguClient.swift @@ -468,6 +468,7 @@ public extension NuguClient { token: String? = nil, source: TextInputSource? = nil, requestType: TextAgentRequestType, + service: [String: AnyHashable]? = nil, completion: ((StreamDataState) -> Void)? = nil ) -> String { dialogStateAggregator.isChipsRequestInProgress = true @@ -476,7 +477,8 @@ public extension NuguClient { text: text, token: token, source: source, - requestType: requestType + requestType: requestType, + service: service ) { [weak self] state in switch state { case .sent: @@ -505,6 +507,7 @@ public extension NuguClient { token: String? = nil, playServiceId: String? = nil, source: TextInputSource? = nil, + service: [String: AnyHashable]? = nil, completion: ((StreamDataState) -> Void)? = nil ) -> String { dialogStateAggregator.isChipsRequestInProgress = true @@ -513,7 +516,8 @@ public extension NuguClient { text: text, token: token, playServiceId: playServiceId, - source: source + source: source, + service: service ) { [weak self] state in switch state { case .sent: From 2512ffa4403f91012fccfd4fdde566115d8e5f1b Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 28 Nov 2023 16:09:09 +0900 Subject: [PATCH 02/24] =?UTF-8?q?ImageAgent=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20(#1111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - TextAgent 업데이트 - version 1.1 - sendImage event field에 service object 추가 --- .../Sources/CapabilityAgents/Image/Image+Event.swift | 4 +++- .../Sources/CapabilityAgents/Image/ImageAgent.swift | 5 +++-- .../CapabilityAgents/Image/ImageAgentProtocol.swift | 10 ++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/Image/Image+Event.swift b/NuguAgents/Sources/CapabilityAgents/Image/Image+Event.swift index 1f6db8e46..ab872cb0d 100644 --- a/NuguAgents/Sources/CapabilityAgents/Image/Image+Event.swift +++ b/NuguAgents/Sources/CapabilityAgents/Image/Image+Event.swift @@ -27,7 +27,7 @@ extension ImageAgent { let referrerDialogRequestId: String? enum TypeInfo { - case sendImage + case sendImage(service: [String: AnyHashable]?) } } @@ -46,6 +46,8 @@ extension ImageAgent.Event: Eventable { var payload: [String: AnyHashable] { var payload: [String: AnyHashable] = [:] switch typeInfo { + case let .sendImage(service): + payload["service"] = service default: break } diff --git a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift index b1eb2e83c..c55f85b83 100644 --- a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift @@ -31,7 +31,7 @@ private enum Const { } public class ImageAgent: ImageAgentProtocol { - public var capabilityAgentProperty: CapabilityAgentProperty = .init(category: .image, version: "1.0") + public var capabilityAgentProperty: CapabilityAgentProperty = .init(category: .image, version: "1.1") // private private let directiveSequencer: DirectiveSequenceable @@ -74,6 +74,7 @@ public class ImageAgent: ImageAgentProtocol { public extension ImageAgent { @discardableResult func requestSendImage( _ image: Data, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? = nil ) -> String { let eventIdentifier = EventIdentifier() @@ -88,7 +89,7 @@ public extension ImageAgent { guard let self = self else { return } self.upstreamDataSender.sendStream( Event( - typeInfo: .sendImage, + typeInfo: .sendImage(service: service), referrerDialogRequestId: nil ).makeEventMessage( property: self.capabilityAgentProperty, diff --git a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift index 7a037452f..7ad14ede0 100644 --- a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift +++ b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift @@ -24,6 +24,16 @@ import NuguCore public protocol ImageAgentProtocol: CapabilityAgentable { @discardableResult func requestSendImage( _ image: Data, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String } + +public extension ImageAgentProtocol { + @discardableResult func requestSendImage( + _ image: Data, + completion: ((StreamDataState) -> Void)? + ) -> String { + requestSendImage(image, service: nil, completion: completion) + } +} From 68761c76510b7f98db55d5783be2906e87ddb051 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 28 Nov 2023 16:11:51 +0900 Subject: [PATCH 03/24] Asr agent room (#1112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - ASRAgent 업데이트 - version 1.8 - recognize event field에 service object 추가 --------- Co-authored-by: childc --- .../ASRAgent+Event.swift | 5 +++-- .../AutomaticSpeechRecognition/ASRAgent.swift | 21 +++++++++++++++---- .../ASRAgentProtocol.swift | 14 ++++++++++++- .../ASRRequest.swift | 1 + 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift index 7439fe7d3..371ad81e7 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift @@ -29,7 +29,7 @@ extension ASRAgent { let referrerDialogRequestId: String? enum TypeInfo { - case recognize(initiator: ASRInitiator, options: ASROptions) + case recognize(initiator: ASRInitiator, options: ASROptions, service: [String: AnyHashable]?) case responseTimeout case listenTimeout case stopRecognize @@ -52,7 +52,7 @@ extension ASRAgent.Event: Eventable { var payload: [String: AnyHashable] { var payload: [String: AnyHashable?] switch typeInfo { - case .recognize(let initiator, let options): + case .recognize(let initiator, let options, let service): payload = [ "codec": "SPEEX", "language": "KOR", @@ -61,6 +61,7 @@ extension ASRAgent.Event: Eventable { "playServiceId": dialogAttributes?["playServiceId"], "domainTypes": dialogAttributes?["domainTypes"], "asrContext": dialogAttributes?["asrContext"], + "service": service, "timeout": [ "listen": options.timeout.truncatedMilliSeconds, "maxSpeech": options.maxDuration.truncatedMilliSeconds, diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift index e06dd4742..869020969 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift @@ -30,7 +30,7 @@ import RxSwift public final class ASRAgent: ASRAgentProtocol { // CapabilityAgentable // TODO: ASR interface version 1.1 -> ASR.Recognize(wakeup/power) - public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .automaticSpeechRecognition, version: "1.7") + public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .automaticSpeechRecognition, version: "1.8") private let playSyncProperty = PlaySyncProperty(layerType: .asr, contextType: .sound) // Private @@ -255,6 +255,7 @@ public final class ASRAgent: ASRAgentProtocol { public extension ASRAgent { @discardableResult func startRecognition( initiator: ASRInitiator, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String { log.debug("startRecognition, initiator: \(initiator)") @@ -268,7 +269,12 @@ public extension ASRAgent { return } - self.startRecognition(initiator: initiator, eventIdentifier: eventIdentifier, completion: completion) + startRecognition( + initiator: initiator, + eventIdentifier: eventIdentifier, + service: service, + completion: completion + ) } return eventIdentifier.dialogRequestId @@ -458,7 +464,12 @@ private extension ASRAgent { } self.asrState = .expectingSpeech - self.startRecognition(initiator: .expectSpeech, eventIdentifier: EventIdentifier(), completion: nil) + startRecognition( + initiator: .expectSpeech, + eventIdentifier: EventIdentifier(), + service: asrRequest?.service, + completion: nil + ) } } } @@ -590,7 +601,7 @@ private extension ASRAgent { } upstreamDataSender.sendStream( Event( - typeInfo: .recognize(initiator: asrRequest.initiator, options: asrRequest.options), + typeInfo: .recognize(initiator: asrRequest.initiator, options: asrRequest.options, service: asrRequest.service), dialogAttributes: dialogAttributeStore.requestAttributes(key: expectSpeech?.messageId), referrerDialogRequestId: asrRequest.referrerDialogRequestId ).makeEventMessage( @@ -677,6 +688,7 @@ private extension ASRAgent { func startRecognition( initiator: ASRInitiator, eventIdentifier: EventIdentifier, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) { let semaphore = DispatchSemaphore(value: 0) @@ -697,6 +709,7 @@ private extension ASRAgent { initiator: initiator, options: options, referrerDialogRequestId: expectSpeech?.dialogRequestId, + service: service, completion: completion ) self.contextManager.getContexts { [weak self] contextPayload in diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift index 53b707712..a007edb3f 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift @@ -39,6 +39,7 @@ public protocol ASRAgentProtocol: CapabilityAgentable, TypedNotifyable { /// - Returns: The dialogRequestId for request. @discardableResult func startRecognition( initiator: ASRInitiator, + service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String @@ -66,6 +67,17 @@ public extension ASRAgentProtocol { /// - options: The options for recognition. /// - Returns: The dialogRequestId for request. @discardableResult func startRecognition(initiator: ASRInitiator) -> String { - return startRecognition(initiator: initiator, completion: nil) + return startRecognition(initiator: initiator, service: nil, completion: nil) + } + + /// This function asks the `ASRAgent` to send a Recognize Event to Server and start streaming from `AudioStream`, which transitions it to the `recognizing` state. + /// + /// This function can be called in `idle` and `expectingSpeech` state. + /// + /// - Parameters: + /// - options: The options for recognition. + /// - Returns: The dialogRequestId for request. + @discardableResult func startRecognition(initiator: ASRInitiator, completion: ((StreamDataState) -> Void)?) -> String { + return startRecognition(initiator: initiator, service: nil, completion: completion) } } diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRRequest.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRRequest.swift index 9f092fcdd..08ad81a3f 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRRequest.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRRequest.swift @@ -27,6 +27,7 @@ struct ASRRequest { let initiator: ASRInitiator let options: ASROptions let referrerDialogRequestId: String? + let service: [String: AnyHashable]? let completion: ((StreamDataState) -> Void)? var contextPayload: [ContextInfo] = [] From 72ce39776b6e2ae040e8ab069506cb1afb3f3d5c Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Wed, 29 Nov 2023 14:30:58 +0900 Subject: [PATCH 04/24] =?UTF-8?q?ASRAgent=20=EB=82=B4=20requestType=20fiel?= =?UTF-8?q?d=20=EC=B6=94=EA=B0=80=20(#1113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - STT 기능 추가 - recognize event 내 requestType field 추가 - notifyMessage directive 내 requestType field 추가 --- .../ASRAgent+Event.swift | 3 ++- .../AutomaticSpeechRecognition/ASRAgent.swift | 9 +++++++-- .../ASRAgentProtocol.swift | 13 +++++++++---- .../ASRNotifyResult.swift | 3 +++ .../ASROptions.swift | 10 +++++++++- .../ASRResult.swift | 2 +- .../Audio/SpeechRecognizerAggregatable.swift | 19 ++++++++++++++++--- .../Audio/SpeechRecognizerAggregator.swift | 9 +++++++-- .../SpeechRecognizerAggregatorState.swift | 11 +++++++++-- 9 files changed, 63 insertions(+), 16 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift index 371ad81e7..98e2a90df 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent+Event.swift @@ -66,7 +66,8 @@ extension ASRAgent.Event: Eventable { "listen": options.timeout.truncatedMilliSeconds, "maxSpeech": options.maxDuration.truncatedMilliSeconds, "response": 10000 - ] + ], + "requestType": options.requestType ] if case let .wakeUpWord(keyword, _, start, end, detection) = initiator { diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift index 869020969..5977a2d7d 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift @@ -256,6 +256,7 @@ public extension ASRAgent { @discardableResult func startRecognition( initiator: ASRInitiator, service: [String: AnyHashable]?, + requestType: String?, completion: ((StreamDataState) -> Void)? ) -> String { log.debug("startRecognition, initiator: \(initiator)") @@ -273,6 +274,7 @@ public extension ASRAgent { initiator: initiator, eventIdentifier: eventIdentifier, service: service, + requestType: requestType, completion: completion ) } @@ -468,6 +470,7 @@ private extension ASRAgent { initiator: .expectSpeech, eventIdentifier: EventIdentifier(), service: asrRequest?.service, + requestType: options.requestType, completion: nil ) } @@ -500,7 +503,7 @@ private extension ASRAgent { case .partial: self.asrResult = .partial(text: item.result ?? "", header: directive.header) case .complete: - self.asrResult = .complete(text: item.result ?? "", header: directive.header) + self.asrResult = .complete(text: item.result ?? "", header: directive.header, requestType: item.requestType) case .none: self.asrResult = .none(header: directive.header) case .error: @@ -689,10 +692,11 @@ private extension ASRAgent { initiator: ASRInitiator, eventIdentifier: EventIdentifier, service: [String: AnyHashable]?, + requestType: String?, completion: ((StreamDataState) -> Void)? ) { let semaphore = DispatchSemaphore(value: 0) - let options: ASROptions + var options: ASROptions if let epd = self.expectSpeech?.payload.epd { options = ASROptions( maxDuration: epd.maxDuration ?? self.options.maxDuration, @@ -704,6 +708,7 @@ private extension ASRAgent { } else { options = self.options } + options.updateRequestType(requestType) asrRequest = ASRRequest( eventIdentifier: eventIdentifier, initiator: initiator, diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift index a007edb3f..09044852d 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift @@ -40,6 +40,7 @@ public protocol ASRAgentProtocol: CapabilityAgentable, TypedNotifyable { @discardableResult func startRecognition( initiator: ASRInitiator, service: [String: AnyHashable]?, + requestType: String?, completion: ((StreamDataState) -> Void)? ) -> String @@ -66,8 +67,8 @@ public extension ASRAgentProtocol { /// - Parameters: /// - options: The options for recognition. /// - Returns: The dialogRequestId for request. - @discardableResult func startRecognition(initiator: ASRInitiator) -> String { - return startRecognition(initiator: initiator, service: nil, completion: nil) + @discardableResult func startRecognition(initiator: ASRInitiator, requestType: String? = nil) -> String { + return startRecognition(initiator: initiator, service: nil, requestType: requestType, completion: nil) } /// This function asks the `ASRAgent` to send a Recognize Event to Server and start streaming from `AudioStream`, which transitions it to the `recognizing` state. @@ -77,7 +78,11 @@ public extension ASRAgentProtocol { /// - Parameters: /// - options: The options for recognition. /// - Returns: The dialogRequestId for request. - @discardableResult func startRecognition(initiator: ASRInitiator, completion: ((StreamDataState) -> Void)?) -> String { - return startRecognition(initiator: initiator, service: nil, completion: completion) + @discardableResult func startRecognition( + initiator: ASRInitiator, + requestType: String? = nil, + completion: ((StreamDataState) -> Void)? + ) -> String { + return startRecognition(initiator: initiator, service: nil, requestType: requestType, completion: completion) } } diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRNotifyResult.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRNotifyResult.swift index 7d7e04906..162d40353 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRNotifyResult.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRNotifyResult.swift @@ -24,6 +24,7 @@ struct ASRNotifyResult { let token: String? let result: String? let state: State + let requestType: String? enum State: String, Decodable { /// 사용자 발화의 일부분 @@ -50,6 +51,7 @@ extension ASRNotifyResult: Decodable { case token case result case state + case requestType } public init(from decoder: Decoder) throws { @@ -57,5 +59,6 @@ extension ASRNotifyResult: Decodable { token = try? container.decode(String.self, forKey: .token) result = try? container.decode(String.self, forKey: .result) state = try container.decode(State.self, forKey: .state) + requestType = try? container.decode(String.self, forKey: .requestType) } } diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASROptions.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASROptions.swift index 5a68b5931..59f2f9b47 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASROptions.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASROptions.swift @@ -38,6 +38,8 @@ public struct ASROptions { /// <#Description#> public let endPointing: EndPointing + public var requestType: String? + /// - Parameters: /// - maxDuration: Max duration from speech start to end. /// - timeout: Max duration of waiting for speech. @@ -47,13 +49,15 @@ public struct ASROptions { timeout: TimeIntervallic = NuguTimeInterval(seconds: 7), pauseLength: TimeIntervallic = NuguTimeInterval(milliseconds: 700), encoding: Encoding = .partial, - endPointing: EndPointing + endPointing: EndPointing, + requestType: String? = nil ) { self.maxDuration = maxDuration self.timeout = timeout self.pauseLength = pauseLength self.encoding = encoding self.endPointing = endPointing + self.requestType = requestType } /// <#Description#> @@ -68,6 +72,10 @@ public struct ASROptions { /// Server side end point detector does not support yet. case server } + + mutating func updateRequestType(_ requestType: String?) { + self.requestType = requestType + } } // MARK: - ASROptions.EndPointing + server value diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRResult.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRResult.swift index 24d0bb8a7..a71f32533 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRResult.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRResult.swift @@ -34,7 +34,7 @@ public enum ASRResult { /// 사용자 발화의 전체 문장 /// - Parameter text: Recognized utterance. /// - Parameter header: The header of the originally handled directive. - case complete(text: String, header: Downstream.Header) + case complete(text: String, header: Downstream.Header, requestType: String? = nil) /// 음성 인식 요청 취소 case cancel(header: Downstream.Header? = nil) /// The `ASR.ExpectSpeech` directive has been cancelled. diff --git a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatable.swift b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatable.swift index 9d752ae29..fa34a3d13 100644 --- a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatable.swift +++ b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatable.swift @@ -31,8 +31,15 @@ public protocol SpeechRecognizerAggregatable: AnyObject { /// Start ASR(`ASRAgentProcotol`) with microphone. /// - Parameters: /// - initiator: The options for recognition. + /// - service: The service object that identifies external options. + /// - requestType: The type of recognition request. /// - completion: The completion handler to call when the request is complete. - func startListening(initiator: ASRInitiator, completion: ((StreamDataState) -> Void)?) + func startListening( + initiator: ASRInitiator, + service: [String: AnyHashable]?, + requestType: String?, + completion: ((StreamDataState) -> Void)? + ) /// Start keyword detector with microphone. func startListeningWithTrigger(completion: ((Result) -> Void)?) @@ -49,8 +56,14 @@ public extension SpeechRecognizerAggregatable { /// Start ASR(`ASRAgentProcotol`) with microphone. /// - Parameters: /// - initiator: The options for recognition. - func startListening(initiator: ASRInitiator) { - startListening(initiator: initiator, completion: nil) + /// - service: The service object that identifies external options. + /// - requestType: The type of recognition request. + func startListening( + initiator: ASRInitiator, + service: [String: AnyHashable]? = nil, + requestType: String? = nil + ) { + startListening(initiator: initiator, service: service, requestType: requestType, completion: nil) } func startListeningWithTrigger() { diff --git a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift index 90641ed03..e8c5d3057 100644 --- a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift +++ b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift @@ -101,7 +101,12 @@ public class SpeechRecognizerAggregator: SpeechRecognizerAggregatable { // MARK: - SpeechRecognizerAggregatable public extension SpeechRecognizerAggregator { - func startListening(initiator: ASRInitiator, completion: ((StreamDataState) -> Void)? = nil) { + func startListening( + initiator: ASRInitiator, + service: [String: AnyHashable]?, + requestType: String?, + completion: ((StreamDataState) -> Void)? = nil + ) { recognizeQueue.async { [weak self] in guard let self else { return } @@ -116,7 +121,7 @@ public extension SpeechRecognizerAggregator { asrAgent.stopRecognition() } - asrAgent.startRecognition(initiator: initiator) { [weak self] state in + asrAgent.startRecognition(initiator: initiator, service: service, requestType: requestType) { [weak self] state in guard case .prepared = state else { completion?(state) return diff --git a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorState.swift b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorState.swift index f0ac1ec71..aa4d11d4e 100644 --- a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorState.swift +++ b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorState.swift @@ -47,6 +47,13 @@ public enum SpeechRecognizerAggregatorState: Equatable { public struct Result { public let type: ResultType public let value: String + public let requestType: String? + + public init(type: ResultType, value: String, requestType: String? = nil) { + self.type = type + self.value = value + self.requestType = requestType + } public enum ResultType { case partial @@ -79,8 +86,8 @@ extension SpeechRecognizerAggregatorState { self = .result(Result(type: .complete, value: "")) case .partial(let text, _): self = .result(Result(type: .partial, value: text)) - case .complete(let text, _): - self = .result(Result(type: .complete, value: text)) + case .complete(let text, _, let requestType): + self = .result(Result(type: .complete, value: text, requestType: requestType)) case .cancel, .cancelExpectSpeech: self = .cancelled case .error(let error, _): From 30bf61264afa866e900257916dadd52a43374844 Mon Sep 17 00:00:00 2001 From: ParkJongSang Date: Wed, 6 Dec 2023 15:01:06 +0900 Subject: [PATCH 05/24] =?UTF-8?q?AudioPlayer=20DelayEvent=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20ceil=EC=9D=B4=20=EC=95=84=EB=8B=8C=20floor=EB=A1=9C?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift index 12d713aca..9680525a4 100644 --- a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift +++ b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift @@ -259,7 +259,7 @@ private extension AudioPlayer { seconds.isInfinite == false else { return 0 } - return Int(ceil(seconds)) + return Int(floor(seconds)) }) .filter { [weak self] offset in guard let self = self else { return false } From 3e97ff162f384c62825389188500e744b13f5baa Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Wed, 3 Jan 2024 12:11:01 +0900 Subject: [PATCH 06/24] =?UTF-8?q?requestSendImage,=20requestTextInput=20?= =?UTF-8?q?=EC=9D=98=20=EB=A6=AC=ED=84=B4=ED=83=80=EC=9E=85=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20(#1115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### description - requestSendImage, requestTextInput 의 리턴타입 업데이트 --- .../CapabilityAgents/Image/ImageAgent.swift | 4 ++-- .../Image/ImageAgentProtocol.swift | 11 +++++++++-- .../CapabilityAgents/Text/TextAgent.swift | 3 +-- .../Text/TextAgentProtocol.swift | 16 +++++++++++++++- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift index c55f85b83..14a54997a 100644 --- a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgent.swift @@ -76,7 +76,7 @@ public extension ImageAgent { _ image: Data, service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? = nil - ) -> String { + ) -> EventIdentifier { let eventIdentifier = EventIdentifier() imageQueue.async { [weak self] in @@ -113,7 +113,7 @@ public extension ImageAgent { } } - return eventIdentifier.dialogRequestId + return eventIdentifier } private func resizeToSuitableResolution(imageData: Data) -> Data? { diff --git a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift index 7ad14ede0..534e243ec 100644 --- a/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift +++ b/NuguAgents/Sources/CapabilityAgents/Image/ImageAgentProtocol.swift @@ -27,13 +27,20 @@ public protocol ImageAgentProtocol: CapabilityAgentable { service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String + + @discardableResult func requestSendImage( + _ image: Data, + service: [String: AnyHashable]?, + completion: ((StreamDataState) -> Void)? + ) -> EventIdentifier } public extension ImageAgentProtocol { @discardableResult func requestSendImage( _ image: Data, - completion: ((StreamDataState) -> Void)? + service: [String: AnyHashable]? = nil, + completion: ((StreamDataState) -> Void)? = nil ) -> String { - requestSendImage(image, service: nil, completion: completion) + requestSendImage(image, service: service, completion: completion).dialogRequestId } } diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift index 1c3d95884..173293db0 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift @@ -142,7 +142,7 @@ extension TextAgent { source: TextInputSource?, service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? - ) -> String { + ) -> EventIdentifier { sendFullContextEvent( textInput( text: text, @@ -153,7 +153,6 @@ extension TextAgent { ), completion: completion ) - .dialogRequestId } } diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift index 1a07dbfac..e361bfaec 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentProtocol.swift @@ -55,6 +55,20 @@ public protocol TextAgentProtocol: CapabilityAgentable { service: [String: AnyHashable]?, completion: ((StreamDataState) -> Void)? ) -> String + + /// Send event that needs a text-based recognition + /// - Parameters: + /// - text: The `text` to be recognized + /// - completion: The completion handler to call when the request is complete. + /// - Returns: The eventIdentifier for request. + @discardableResult func requestTextInput( + text: String, + token: String?, + playServiceId: String?, + source: TextInputSource?, + service: [String: AnyHashable]?, + completion: ((StreamDataState) -> Void)? + ) -> EventIdentifier } // MARK: - Default @@ -110,6 +124,6 @@ public extension TextAgentProtocol { source: source, service: service, completion: completion - ) + ).dialogRequestId } } From c22daed50d7f8362ccbef3a1c9a017bdaad32ff8 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Wed, 3 Jan 2024 13:56:49 +0900 Subject: [PATCH 07/24] =?UTF-8?q?NuguClient=20=EB=82=B4=20requestTextInput?= =?UTF-8?q?=20overloading=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#1116)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - NuguClient 내 requestTextInput overloading 메서드 추가 --- NuguClientKit/Sources/Client/NuguClient.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/NuguClientKit/Sources/Client/NuguClient.swift b/NuguClientKit/Sources/Client/NuguClient.swift index b746e4717..e2ad63857 100644 --- a/NuguClientKit/Sources/Client/NuguClient.swift +++ b/NuguClientKit/Sources/Client/NuguClient.swift @@ -510,6 +510,35 @@ public extension NuguClient { service: [String: AnyHashable]? = nil, completion: ((StreamDataState) -> Void)? = nil ) -> String { + requestTextInput( + text: text, + token: token, + playServiceId: playServiceId, + source: source, + service: service, + completion: completion + ).dialogRequestId + } + + /// Send event that needs a text-based recognition + /// + /// This function cancel speech recognition.(e.g. `ASRAgentProtocol.startRecognition(:initiator)`) + /// Use `NuguClient.textAgent.requestTextInput` directly to request independent of speech recognition. + /// + /// - Parameters: + /// - text: The `text` to be recognized + /// - token: token + /// - requestType: `TextAgentRequestType` + /// - completion: The completion handler to call when the request is complete + /// - Returns: The eventIdentifier for request. + @discardableResult func requestTextInput( + text: String, + token: String? = nil, + playServiceId: String? = nil, + source: TextInputSource? = nil, + service: [String: AnyHashable]? = nil, + completion: ((StreamDataState) -> Void)? = nil + ) -> EventIdentifier { dialogStateAggregator.isChipsRequestInProgress = true return textAgent.requestTextInput( From 34a77a72e9dc2e103fbb6258683759ffc8659bd4 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Thu, 4 Jan 2024 17:12:14 +0900 Subject: [PATCH 08/24] =?UTF-8?q?asrState=EA=B0=80=20idle=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EB=90=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#1117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - asrResult가 none이나 completed state로 변경 시 idle로 변경하도록 수정 ### Focus - startStream이 finished completion을 전달 받을 때 idle상태로 변경되면 directive 처리가 되기 이전에 asrState가 idle 로 변경되는 문제로 인해 asrResult의 state에 따른 asrState처리 --- .../AutomaticSpeechRecognition/ASRAgent.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift index 5977a2d7d..f40615f36 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift @@ -98,10 +98,12 @@ public final class ASRAgent: ASRAgentProtocol { // `ASRState` -> Event -> `expectSpeechDirective` -> `ASRAgentDelegate` switch asrResult { case .none: + asrState = .idle expectSpeech = nil case .partial: break case .complete: + asrState = .idle expectSpeech = nil case .cancel: asrState = .idle @@ -617,8 +619,6 @@ private extension ASRAgent { guard self?.asrRequest?.eventIdentifier == asrRequest.eventIdentifier else { return } switch state { - case .finished: - self?.asrState = .idle case .error(let error): self?.asrResult = .error(error) case .sent: From 6ffcb07bbb50e88e6ad5191af3ebfe8552226483 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 9 Jan 2024 10:18:56 +0900 Subject: [PATCH 09/24] =?UTF-8?q?ExpectSpeech=20directive=20payload?= =?UTF-8?q?=EC=9D=98=20service=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20directive=EC=88=98=EC=8B=A0=20=ED=9B=84=20startReco?= =?UTF-8?q?gnition=20=ED=95=B8=EB=93=A4=EB=A7=81=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#1118)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - ExpectSpeech directive payload의 service필드 추가 및 directive수신 후 startRecognition 핸들링 로직 추가 --- .../AutomaticSpeechRecognition/ASRAgent.swift | 15 +++++++--- .../ASRAgentDelegate.swift | 29 +++++++++++++++++++ .../ASRAgentProtocol.swift | 1 + .../ASRExpectSpeech.swift | 3 ++ nugu-ios.xcodeproj/project.pbxproj | 6 +++- 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentDelegate.swift diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift index f40615f36..0dce37256 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgent.swift @@ -29,10 +29,11 @@ import RxSwift public final class ASRAgent: ASRAgentProtocol { // CapabilityAgentable - // TODO: ASR interface version 1.1 -> ASR.Recognize(wakeup/power) public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .automaticSpeechRecognition, version: "1.8") private let playSyncProperty = PlaySyncProperty(layerType: .asr, contextType: .sound) + public weak var delegate: ASRAgentDelegate? + // Private private let focusManager: FocusManageable private let contextManager: ContextManageable @@ -93,7 +94,7 @@ public final class ASRAgent: ASRAgentProtocol { log.error("ASR request: \(String(describing: asrRequest)), result: \(String(describing: asrResult))") return } - log.info("\(asrResult)") + log.info("asrResult: \(asrResult)") // `ASRState` -> Event -> `expectSpeechDirective` -> `ASRAgentDelegate` switch asrResult { @@ -455,7 +456,7 @@ private extension ASRAgent { defer { completion(.finished) } self?.asrDispatchQueue.sync { [weak self] in - guard let self = self else { return } + guard let self = self, let delegate = self.delegate else { return } // ex> TTS 도중 stopRecognition 호출. guard let expectSpeech = self.expectSpeech, expectSpeech.messageId == directive.header.messageId else { log.info("Message id does not match") @@ -466,12 +467,18 @@ private extension ASRAgent { log.warning("ExpectSpeech only allowed in IDLE or BUSY state.") return } + let service = expectSpeech.payload.service + guard service == nil || delegate.asrAgentWillStartExpectSpeech(service: service) else { + log.warning("ExpectSpeech service field is not nil. service: \(String(describing: service))") + self.asrResult = nil + return + } self.asrState = .expectingSpeech startRecognition( initiator: .expectSpeech, eventIdentifier: EventIdentifier(), - service: asrRequest?.service, + service: service, requestType: options.requestType, completion: nil ) diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentDelegate.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentDelegate.swift new file mode 100644 index 000000000..663d46ddb --- /dev/null +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentDelegate.swift @@ -0,0 +1,29 @@ +// +// ASRAgentDelegate.swift +// NuguAgents +// +// Created by Jaycesub on 17/04/2019. +// Copyright (c) 2024 SK Telecom Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public protocol ASRAgentDelegate: AnyObject { + /// ASRAgent start recognition after receiving expectSpeech directive. + /// Returns true if start recognition without being affected by the condition. + /// - Parameter service: The service object included in expectSpeech payload + /// - Returns: True if the ASRAgent start recognition after receiving expectSpeech directive. + func asrAgentWillStartExpectSpeech(service: [String: AnyHashable]?) -> Bool +} diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift index 09044852d..9b5055568 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRAgentProtocol.swift @@ -26,6 +26,7 @@ import NuguUtils /// ASR (AutomaticSpeechRecognition) is responsible for capturing the audio and delivering it to the server and receiving the result of speech recognition. public protocol ASRAgentProtocol: CapabilityAgentable, TypedNotifyable { + var delegate: ASRAgentDelegate? { get set } var options: ASROptions { get set } var asrState: ASRState { get } diff --git a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRExpectSpeech.swift b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRExpectSpeech.swift index 434388a7a..c901637b5 100644 --- a/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRExpectSpeech.swift +++ b/NuguAgents/Sources/CapabilityAgents/AutomaticSpeechRecognition/ASRExpectSpeech.swift @@ -37,6 +37,7 @@ struct ASRExpectSpeech { let asrContext: [String: AnyHashable]? let epd: EPD? let listenTimeoutFailBeep: Bool? + let service: [String: AnyHashable]? struct EPD: Decodable { let timeoutMilliseconds: Int? @@ -55,6 +56,7 @@ extension ASRExpectSpeech.Payload: Decodable { case asrContext case epd case listenTimeoutFailBeep + case service } public init(from decoder: Decoder) throws { @@ -64,6 +66,7 @@ extension ASRExpectSpeech.Payload: Decodable { asrContext = try? container.decodeIfPresent([String: AnyHashable].self, forKey: .asrContext) epd = try? container.decode(EPD.self, forKey: .epd) listenTimeoutFailBeep = try? container.decode(Bool.self, forKey: .listenTimeoutFailBeep) + service = try? container.decodeIfPresent([String: AnyHashable].self, forKey: .service) } } diff --git a/nugu-ios.xcodeproj/project.pbxproj b/nugu-ios.xcodeproj/project.pbxproj index 5cea510b4..09e5543ec 100644 --- a/nugu-ios.xcodeproj/project.pbxproj +++ b/nugu-ios.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -529,6 +529,7 @@ E649868A28337649001AD733 /* ControlCenterManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E649868928337649001AD733 /* ControlCenterManager.swift */; }; E6BE047C2A0BA1A400AE29E3 /* Image+Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6BE047B2A0BA1A400AE29E3 /* Image+Event.swift */; }; E6BE047E2A0BA1BD00AE29E3 /* ImageAgent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6BE047D2A0BA1BD00AE29E3 /* ImageAgent.swift */; }; + E6F3DCBD2B4CB9D800298A20 /* ASRAgentDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F3DCBC2B4CB9D800298A20 /* ASRAgentDelegate.swift */; }; E6FBA02B2A1379C500AF8B05 /* MessengerAgentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6FBA02A2A1379C400AF8B05 /* MessengerAgentProtocol.swift */; }; E6FBA02D2A137A9700AF8B05 /* ImageAgentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6FBA02C2A137A9700AF8B05 /* ImageAgentProtocol.swift */; }; F6A6B0842AAEFDD900D41DEC /* AudioPlayer2Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A6B0832AAEFDD900D41DEC /* AudioPlayer2Template.swift */; }; @@ -1331,6 +1332,7 @@ E649868928337649001AD733 /* ControlCenterManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlCenterManager.swift; sourceTree = ""; }; E6BE047B2A0BA1A400AE29E3 /* Image+Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Image+Event.swift"; sourceTree = ""; }; E6BE047D2A0BA1BD00AE29E3 /* ImageAgent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageAgent.swift; sourceTree = ""; }; + E6F3DCBC2B4CB9D800298A20 /* ASRAgentDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASRAgentDelegate.swift; sourceTree = ""; }; E6FBA02A2A1379C400AF8B05 /* MessengerAgentProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessengerAgentProtocol.swift; sourceTree = ""; }; E6FBA02C2A137A9700AF8B05 /* ImageAgentProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageAgentProtocol.swift; sourceTree = ""; }; F6A6B0832AAEFDD900D41DEC /* AudioPlayer2Template.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer2Template.swift; sourceTree = ""; }; @@ -2080,6 +2082,7 @@ 737388F024A46C7F0018DDD2 /* ASROptions.swift */, 737388F124A46C7F0018DDD2 /* ASRResult.swift */, 737388F224A46C7F0018DDD2 /* ASRState.swift */, + E6F3DCBC2B4CB9D800298A20 /* ASRAgentDelegate.swift */, ); path = AutomaticSpeechRecognition; sourceTree = ""; @@ -4512,6 +4515,7 @@ 73152FCC23E0415B00F843C3 /* DisplayRenderingInfo.swift in Sources */, 736508652462F7FA00EF4549 /* SktOpusParser.swift in Sources */, 7E284D9C24DD2B4A00BF9640 /* InteractionControlManager.swift in Sources */, + E6F3DCBD2B4CB9D800298A20 /* ASRAgentDelegate.swift in Sources */, 1FD5D19124C82F3B007BA384 /* MediaPlayerAgent+Event.swift in Sources */, 737388C924A46C1D0018DDD2 /* ChipsAgentProtocol.swift in Sources */, 1FD5D17E24A99993007BA384 /* PhoneCallType.swift in Sources */, From 37394c128b3d42ca4554522cc6d98e8dfc81710b Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Wed, 10 Jan 2024 07:47:41 +0900 Subject: [PATCH 10/24] =?UTF-8?q?=20Text.Redirect,=20Text.ExpectTyping=20d?= =?UTF-8?q?irective=20payload=EC=97=90=20service=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20textInput=20Event=EC=97=90=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8=EC=8B=9C=ED=82=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#1119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - Text.Redirect, Text.ExpectTyping directive payload에 service 필드 추가 및 textInput Event에 포함시키도록 변경 --- .../CapabilityAgents/Text/TextAgent.swift | 4 +++- .../Text/TextAgentExpectTyping.swift | 5 ++++- .../Text/TextAgentRedirectPayload.swift | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift index 173293db0..077eef0b4 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift @@ -261,6 +261,7 @@ private extension TextAgent { text: payload.text, token: payload.token, requestType: requestType, + service: payload.service, referrerDialogRequestId: directive.header.dialogRequestId ), completion: interactionHandler) } @@ -438,7 +439,8 @@ private extension TextAgentExpectTyping.Payload { return [ "asrContext": asrContext, "domainTypes": domainTypes, - "playServiceId": playServiceId + "playServiceId": playServiceId, + "service": service ] } } diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentExpectTyping.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentExpectTyping.swift index 94eefdd10..ed364d406 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentExpectTyping.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentExpectTyping.swift @@ -33,18 +33,21 @@ extension TextAgentExpectTyping { let playServiceId: String? let domainTypes: [AnyHashable]? let asrContext: [String: AnyHashable]? + let service: [String: AnyHashable]? enum CodingKeys: String, CodingKey { case playServiceId case domainTypes case asrContext + case service } - public init(from decoder: Decoder) throws { + init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) playServiceId = try? container.decode(String.self, forKey: .playServiceId) domainTypes = try? container.decode([AnyHashable].self, forKey: .domainTypes) asrContext = try? container.decodeIfPresent([String: AnyHashable].self, forKey: .asrContext) + service = try? container.decodeIfPresent([String: AnyHashable].self, forKey: .service) } } } diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentRedirectPayload.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentRedirectPayload.swift index 606e9eb4a..0e78ea509 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentRedirectPayload.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentRedirectPayload.swift @@ -27,4 +27,26 @@ struct TextAgentRedirectPayload: Decodable { let playServiceId: String let targetPlayServiceId: String? let interactionControl: InteractionControl? + let service: [String: AnyHashable]? + + enum CodingKeys: String, CodingKey { + case text + case token + case source + case playServiceId + case targetPlayServiceId + case interactionControl + case service + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + text = try container.decode(String.self, forKey: .text) + token = try container.decode(String.self, forKey: .token) + source = try? container.decodeIfPresent(String.self, forKey: .source) + playServiceId = try container.decode(String.self, forKey: .playServiceId) + targetPlayServiceId = try? container.decodeIfPresent(String.self, forKey: .targetPlayServiceId) + interactionControl = try? container.decodeIfPresent(InteractionControl.self, forKey: .interactionControl) + service = try? container.decodeIfPresent([String: AnyHashable].self, forKey: .service) + } } From 41ff8fac9d3a3ed2665da6e583ddfe529802251e Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 16 Jan 2024 10:19:31 +0900 Subject: [PATCH 11/24] =?UTF-8?q?recognitionWithTriggerContext=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88=EB=8A=94=20delegate?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#1120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - recognitionWithTriggerContext를 받을 수 있는 delegate 추가 --- .../Audio/SpeechRecognizerAggregator.swift | 16 ++++++++++++++-- .../SpeechRecognizerAggregatorDelegate.swift | 1 + NuguClientKit/Sources/Client/NuguClient.swift | 4 ++++ .../Sources/Client/NuguClientDelegate.swift | 3 +++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift index e8c5d3057..edf14e7f8 100644 --- a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift +++ b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregator.swift @@ -277,13 +277,25 @@ extension SpeechRecognizerAggregator: KeywordDetectorDelegate { public func keywordDetectorDidDetect(keyword: String?, data: Data, start: Int, end: Int, detection: Int) { state = .wakeup(initiator: .wakeUpWord(keyword: keyword, data: data, start: start, end: end, detection: detection)) - asrAgent.startRecognition(initiator: .wakeUpWord( + var service: [String: AnyHashable]? + var requestType: String? + if let context = delegate?.speechRecognizerRequestRecognitionContext() { + service = context["service"] as? [String: AnyHashable] + requestType = context["requestType"] as? String + } + let initiator: ASRInitiator = .wakeUpWord( keyword: keyword, data: data, start: start, end: end, detection: detection - )) + ) + asrAgent.startRecognition( + initiator: initiator, + service: service, + requestType: requestType, + completion: nil + ) } public func keywordDetectorStateDidChange(_ state: KeywordDetectorState) { diff --git a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorDelegate.swift b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorDelegate.swift index f3ed9a624..4b746143a 100644 --- a/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorDelegate.swift +++ b/NuguClientKit/Sources/Audio/SpeechRecognizerAggregatorDelegate.swift @@ -24,4 +24,5 @@ import NuguAgents public protocol SpeechRecognizerAggregatorDelegate: AnyObject { func speechRecognizerStateDidChange(_ state: SpeechRecognizerAggregatorState) + func speechRecognizerRequestRecognitionContext() -> [String: AnyHashable] } diff --git a/NuguClientKit/Sources/Client/NuguClient.swift b/NuguClientKit/Sources/Client/NuguClient.swift index e2ad63857..1f55f0d2e 100644 --- a/NuguClientKit/Sources/Client/NuguClient.swift +++ b/NuguClientKit/Sources/Client/NuguClient.swift @@ -776,6 +776,10 @@ extension NuguClient: SpeechRecognizerAggregatorDelegate { delegate?.nuguClientDidChangeSpeechState(state) notificationCenter.post(name: NuguClient.speechStateChangedNotification, object: self, userInfo: ["state": state]) } + + public func speechRecognizerRequestRecognitionContext() -> [String : AnyHashable] { + delegate?.nuguClientRequestRecognitionWithTriggerContext() ?? [:] + } } extension NuguClient { diff --git a/NuguClientKit/Sources/Client/NuguClientDelegate.swift b/NuguClientKit/Sources/Client/NuguClientDelegate.swift index c3af43dac..762a627f8 100644 --- a/NuguClientKit/Sources/Client/NuguClientDelegate.swift +++ b/NuguClientKit/Sources/Client/NuguClientDelegate.swift @@ -82,6 +82,8 @@ public protocol NuguClientDelegate: AnyObject { /// Notify that nugu client server initiated directive state has been changed /// - Parameter state: ServerSideEventReceiverState func nuguClientServerInitiatedDirectiveRecevierStateDidChange(_ state: ServerSideEventReceiverState) + + func nuguClientRequestRecognitionWithTriggerContext() -> [String: AnyHashable] } // MARK: - Optional @@ -109,4 +111,5 @@ public extension NuguClientDelegate { func nuguClientDidSend(event: Event, error: Error?) {} func nuguClientDidSend(attachment: EventAttachment, error: Error?) {} func nuguClientServerInitiatedDirectiveRecevierStateDidChange(_ state: ServerSideEventReceiverState) {} + func nuguClientRequestRecognitionWithTriggerContext() -> [String: AnyHashable] { [:] } } From 65f15483b9b01ed14e1b0b563368a1fbe8978172 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 16 Jan 2024 14:04:22 +0900 Subject: [PATCH 12/24] =?UTF-8?q?NuguBuilder=EC=97=90=20messengerAgent?= =?UTF-8?q?=EB=A5=BC=20default=20agent=EC=97=90=EC=84=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#1121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - NuguBuilder에 messengerAgent를 default agent에서 제거 --- .../Sources/Client/NuguClient+Builder.swift | 17 +++++++++++------ NuguClientKit/Sources/Client/NuguClient.swift | 15 +++++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/NuguClientKit/Sources/Client/NuguClient+Builder.swift b/NuguClientKit/Sources/Client/NuguClient+Builder.swift index 5dba2bc31..45a1f28a1 100644 --- a/NuguClientKit/Sources/Client/NuguClient+Builder.swift +++ b/NuguClientKit/Sources/Client/NuguClient+Builder.swift @@ -103,12 +103,6 @@ public extension NuguClient { playSyncManager: playSyncManager ) - public lazy var messengerAgent: MessengerAgentProtocol = MessengerAgent( - directiveSequencer: directiveSequencer, - contextManager: contextManager, - upstreamDataSender: streamDataRouter - ) - public lazy var imageAgent: ImageAgentProtocol = ImageAgent( directiveSequencer: directiveSequencer, contextManager: contextManager, @@ -163,6 +157,8 @@ public extension NuguClient { */ public var displayAgent: DisplayAgentProtocol? + public var messengerAgent: MessengerAgentProtocol? + // Supports /** AudioSessionManager. @@ -277,6 +273,15 @@ public extension NuguClient { alertsAgent?.delegate = delegate as? AlertsAgentDelegate } + if delegate is MessengerAgentDelegate { + messengerAgent = MessengerAgent( + directiveSequencer: directiveSequencer, + contextManager: contextManager, + upstreamDataSender: streamDataRouter + ) + messengerAgent?.delegate = delegate as? MessengerAgentDelegate + } + return self } diff --git a/NuguClientKit/Sources/Client/NuguClient.swift b/NuguClientKit/Sources/Client/NuguClient.swift index 1f55f0d2e..c3b92cb97 100644 --- a/NuguClientKit/Sources/Client/NuguClient.swift +++ b/NuguClientKit/Sources/Client/NuguClient.swift @@ -148,8 +148,6 @@ public class NuguClient { */ public let nudgeAgent: NudgeAgentProtocol - public let messengerAgent: MessengerAgentProtocol - public let imageAgent: ImageAgentProtocol // Additional Agents @@ -236,6 +234,12 @@ public class NuguClient { upstreamDataSender: streamDataRouter ) + public lazy var messengerAgent: MessengerAgentProtocol = MessengerAgent( + directiveSequencer: directiveSequencer, + contextManager: contextManager, + upstreamDataSender: streamDataRouter + ) + // Supports /** Indicates the dialog state. @@ -312,7 +316,7 @@ public class NuguClient { permissionAgent: PermissionAgentProtocol?, alertsAgent: AlertsAgentProtocol?, nudgeAgent: NudgeAgentProtocol, - messengerAgent: MessengerAgentProtocol, + messengerAgent: MessengerAgentProtocol?, imageAgent: ImageAgentProtocol ) { // Core @@ -352,7 +356,6 @@ public class NuguClient { self.utilityAgent = utilityAgent self.routineAgent = routineAgent self.nudgeAgent = nudgeAgent - self.messengerAgent = messengerAgent self.imageAgent = imageAgent // Supports @@ -398,6 +401,10 @@ public class NuguClient { self.alertsAgent = alertsAgent } + if let messengerAgent = messengerAgent { + self.messengerAgent = messengerAgent + } + // Wiring setupAudioSessionManager() setupSpeechRecognizerAggregator() From 820d5c68e93a1e8589678617152a91f9b3a0626e Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Thu, 18 Jan 2024 14:08:10 +0900 Subject: [PATCH 13/24] =?UTF-8?q?Text.TextSource=20Directive=20=EB=82=B4?= =?UTF-8?q?=20service=20object=20field=20=EC=B6=94=EA=B0=80=20(#1122)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - Text.TextSource Directive 업데이트(service object 추가) --- .../CapabilityAgents/Text/TextAgent.swift | 1 + .../Text/TextAgentSourceItem.swift | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift index 077eef0b4..c5ff12662 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgent.swift @@ -197,6 +197,7 @@ private extension TextAgent { text: payload.text, token: payload.token, requestType: requestType, + service: payload.service, referrerDialogRequestId: directive.header.dialogRequestId )) } diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift index 89ae4528e..a3683ebff 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift @@ -24,4 +24,20 @@ struct TextAgentSourceItem: Decodable { let text: String let token: String let playServiceId: String? + let service: [String: AnyHashable]? + + enum CodingKeys: String, CodingKey { + case text + case token + case playServiceId + case service + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + text = try container.decode(String.self, forKey: .text) + token = try container.decode(String.self, forKey: .token) + playServiceId = try container.decode(String.self, forKey: .playServiceId) + service = try? container.decodeIfPresent([String: AnyHashable].self, forKey: .service) + } } From 035b3690733014ec66b7199f7efc52727576191f Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 23 Jan 2024 14:44:34 +0900 Subject: [PATCH 14/24] =?UTF-8?q?Text.TextSource=20Directive=20=EB=94=94?= =?UTF-8?q?=EC=BD=94=EB=94=A9=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#1123)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - Text.TextSource Directive 디코딩 에러 수정 --------- Co-authored-by: latium --- .../Sources/CapabilityAgents/Text/TextAgentSourceItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift index a3683ebff..ca41559c5 100644 --- a/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift +++ b/NuguAgents/Sources/CapabilityAgents/Text/TextAgentSourceItem.swift @@ -37,7 +37,7 @@ struct TextAgentSourceItem: Decodable { let container = try decoder.container(keyedBy: CodingKeys.self) text = try container.decode(String.self, forKey: .text) token = try container.decode(String.self, forKey: .token) - playServiceId = try container.decode(String.self, forKey: .playServiceId) + playServiceId = try container.decodeIfPresent(String.self, forKey: .playServiceId) service = try? container.decodeIfPresent([String: AnyHashable].self, forKey: .service) } } From 110bc6d2bec4ce95121929fd15cb9a36cb535585 Mon Sep 17 00:00:00 2001 From: ParkJongSang Date: Wed, 31 Jan 2024 10:23:56 +0900 Subject: [PATCH 15/24] =?UTF-8?q?ProgressReportDelayElapsed=20=EC=9E=AC?= =?UTF-8?q?=EC=83=9D=EC=A4=91=EC=97=90=20=ED=95=9C=EB=B2=88=EB=A7=8C=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift index 9680525a4..fd33866cd 100644 --- a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift +++ b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayer.swift @@ -64,6 +64,7 @@ final class AudioPlayer { private var lastReportedOffset: Int = 0 private var lastDataAppended = false + private var canReportDelayEvent: Bool = true init(directive: Downstream.Directive) throws { payload = try JSONDecoder().decode(AudioPlayerPlayPayload.self, from: directive.payload) @@ -277,7 +278,8 @@ private extension AudioPlayer { // Check if there is any report target between last offset and current offset. let offsetRange = (self.lastReportedOffset + 1...offset) - if delayReportTime > 0, offsetRange.contains(delayReportTime) { + if delayReportTime > 0, offsetRange.contains(delayReportTime), canReportDelayEvent { + self.canReportDelayEvent = false self.progressDelegate?.audioPlayerDidReportDelay(self) } if intervalReportTime > 0, offsetRange.contains(intervalReportTime * (self.lastReportedOffset / intervalReportTime + 1)) { From 482912a9e950a8be700d7ced7b634167343a379b Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 13 Feb 2024 13:52:09 +0900 Subject: [PATCH 16/24] =?UTF-8?q?RoutineAgentDelegate=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20(#1127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - RoutineAgentDelegate 업데이트 --- .../Sources/CapabilityAgents/Routine/RoutineAgent.swift | 1 + .../CapabilityAgents/Routine/RoutineAgentDelegate.swift | 1 + SampleApp/Sources/UI/NuguServiceWebViewController.swift | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgent.swift b/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgent.swift index b57a32d29..34313441d 100644 --- a/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgent.swift @@ -255,6 +255,7 @@ private extension RoutineAgent { payload: payload ) + self?.delegate?.routineAgentWillStart(item: routine) self?.routineExecuter.start(routine) } } diff --git a/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgentDelegate.swift b/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgentDelegate.swift index b373d845b..8f2e40983 100644 --- a/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgentDelegate.swift +++ b/NuguAgents/Sources/CapabilityAgents/Routine/RoutineAgentDelegate.swift @@ -21,6 +21,7 @@ import Foundation public protocol RoutineAgentDelegate: AnyObject { + func routineAgentWillStart(item: RoutineItem) func routineAgentDidChange(state: RoutineState, item: RoutineItem?) func routineAgentWillProcessAction(_ action: RoutineItem.Payload.Action) func routineAgentDidStopProcessingAction(_ action: RoutineItem.Payload.Action) diff --git a/SampleApp/Sources/UI/NuguServiceWebViewController.swift b/SampleApp/Sources/UI/NuguServiceWebViewController.swift index 0ffddef9e..f859eabfd 100644 --- a/SampleApp/Sources/UI/NuguServiceWebViewController.swift +++ b/SampleApp/Sources/UI/NuguServiceWebViewController.swift @@ -135,6 +135,10 @@ private extension NuguServiceWebViewController { } extension NuguServiceWebViewController: RoutineAgentDelegate { + func routineAgentWillStart(item: NuguAgents.RoutineItem) { + log.debug("routineAgentWillStart") + } + func routineAgentWillProcessAction(_ action: NuguAgents.RoutineItem.Payload.Action) { log.debug("routineAgentWillProcessAction, action: \(action)") } From 04ed7745e99516fdf5af3ddcce49dea7ee5c1a17 Mon Sep 17 00:00:00 2001 From: Sokiwar Date: Wed, 14 Feb 2024 11:13:46 +0900 Subject: [PATCH 17/24] =?UTF-8?q?ControlCenterManager=EC=97=90=20offset?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=A0=81=EC=9A=A9=20(#1128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - ControlCenterManager에 offset업데이트 할 수 있도록 적용 --- .../Sources/Audio/ControlCenterManager.swift | 14 +++++++++++++- .../Presenter/AudioDisplayViewPresenter.swift | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/NuguClientKit/Sources/Audio/ControlCenterManager.swift b/NuguClientKit/Sources/Audio/ControlCenterManager.swift index f98f85351..079161bd3 100644 --- a/NuguClientKit/Sources/Audio/ControlCenterManager.swift +++ b/NuguClientKit/Sources/Audio/ControlCenterManager.swift @@ -134,7 +134,7 @@ public extension ControlCenterManager { } } - func update(_ duration: Int) { + func update(duration: Int) { nowPlayInfoCenterQueue.async { [weak self] in guard let self = self else { return } @@ -145,6 +145,18 @@ public extension ControlCenterManager { } } + func update(offset: Int) { + nowPlayInfoCenterQueue.async { [weak self] in + guard let self else { return } + + var nowPlayingInfoForUpdate = self.nowPlayingInfo + nowPlayingInfoForUpdate[MPNowPlayingInfoPropertyElapsedPlaybackTime] = offset + nowPlayingInfoForUpdate[MPNowPlayingInfoPropertyPlaybackRate] = 1.0 + + self.nowPlayingInfo = nowPlayingInfoForUpdate + } + } + func remove() { nowPlayInfoCenterQueue.async { [weak self] in self?.mediaArtWorkDownloadDataTask?.cancel() diff --git a/NuguClientKit/Sources/Presenter/AudioDisplayViewPresenter.swift b/NuguClientKit/Sources/Presenter/AudioDisplayViewPresenter.swift index 4c5027b28..4ccc51ddc 100644 --- a/NuguClientKit/Sources/Presenter/AudioDisplayViewPresenter.swift +++ b/NuguClientKit/Sources/Presenter/AudioDisplayViewPresenter.swift @@ -309,7 +309,7 @@ private extension AudioDisplayViewPresenter { } audioPlayerDurationObserver = object.observe(NuguAgentNotification.AudioPlayer.Duration.self, queue: .main) { [weak self] (notification) in - self?.controlCenterManager?.update(notification.duration) + self?.controlCenterManager?.update(duration: notification.duration) } } From 7c8d3127e7ce5989cdf72a898de839ebf1108b6d Mon Sep 17 00:00:00 2001 From: childc Date: Tue, 20 Feb 2024 15:35:46 +0900 Subject: [PATCH 18/24] =?UTF-8?q?AudioFocus=20Release(Deactivate)=EA=B0=80?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=20=EC=98=88=EC=A0=95=EB=90=98=EC=96=B4?= =?UTF-8?q?=EC=9E=88=EC=9C=BC=EB=A9=B4=20=EB=8B=A4=EC=8B=9C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20(#1129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NuguClientKit/Sources/Client/NuguClient.swift | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/NuguClientKit/Sources/Client/NuguClient.swift b/NuguClientKit/Sources/Client/NuguClient.swift index c3b92cb97..65bb1eed1 100644 --- a/NuguClientKit/Sources/Client/NuguClient.swift +++ b/NuguClientKit/Sources/Client/NuguClient.swift @@ -284,6 +284,7 @@ public class NuguClient { private let backgroundFocusHolder: BackgroundFocusHolder private var audioDeactivateWorkItem: DispatchWorkItem? private let directiveConnectionQueue = DispatchQueue(label: "com.sktelecom.romaine.NuguClientKit.directive_connection") + private let audioFocusQueue = DispatchQueue(label: "com.sktelecom.romaine.NuguClientKit.audio_focus") init( contextManager: ContextManageable, @@ -593,8 +594,12 @@ extension NuguClient: FocusDelegate { return delegate?.nuguClientShouldUpdateAudioSessionForFocusAquire() == true } - if let audioDeactivateWorkItem = audioDeactivateWorkItem { - audioDeactivateWorkItem.cancel() + audioFocusQueue.async { [weak self] in + guard let self else { return } + if let audioDeactivateWorkItem = audioDeactivateWorkItem { + audioDeactivateWorkItem.cancel() + self.audioDeactivateWorkItem = nil + } } return audioSessionManager.updateAudioSession(requestingFocus: true) == true @@ -606,12 +611,21 @@ extension NuguClient: FocusDelegate { return } - let audioDeactivateWorkItem = DispatchWorkItem { - audioSessionManager.notifyAudioSessionDeactivation() + audioFocusQueue.async { [weak self] in + guard let self else { return } + + // 이미 Release(Deactivate)가 예정되어있으면 다시 등록하지 않음. + guard audioDeactivateWorkItem == nil else { return } + + let audioDeactivateWorkItem = DispatchWorkItem { [weak self] in + audioSessionManager.notifyAudioSessionDeactivation() + self?.audioDeactivateWorkItem = nil + } + + audioFocusQueue.asyncAfter(deadline: .now() + NuguClientConst.audioSessionDeactivationDelay, execute: audioDeactivateWorkItem) + self.audioDeactivateWorkItem = audioDeactivateWorkItem } - - DispatchQueue.global().asyncAfter(deadline: .now() + NuguClientConst.audioSessionDeactivationDelay, execute: audioDeactivateWorkItem) - self.audioDeactivateWorkItem = audioDeactivateWorkItem + } } From 61ef054461902dd790ddf6794cb3405bc57832a9 Mon Sep 17 00:00:00 2001 From: childc Date: Tue, 20 Feb 2024 15:37:24 +0900 Subject: [PATCH 19/24] =?UTF-8?q?`ServerSentEvent`=EC=9D=98=20state?= =?UTF-8?q?=EC=99=80=20PingDisposable=EC=9D=98=20Race=20condition=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20(#1126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Crash Report image ## Description - `ServerSentEvent`를 `ServerSideEvent`로 오기한 부분 바로잡았습니다 (https://github.com/nugu-developers/nugu-ios/pull/1126/commits/e51f1f70bc9d434ca426f30b338b2064094bbd21) - Crash 관련 수정은 https://github.com/nugu-developers/nugu-ios/pull/1126/commits/de08bf93874e2bf476f962a52ca58aa21c7bd214 입니다 --- .../Sources/Client/NuguClientDelegate.swift | 8 +-- .../Sources/Network/Api/NuguApiProvider.swift | 22 +++--- ...r.swift => ServerSentEventProcessor.swift} | 4 +- ...er.swift => ServerSentEventReceiver.swift} | 70 ++++++++++++------- ...ift => ServerSentEventReceiverState.swift} | 10 +-- .../Sources/StreamData/StreamDataRouter.swift | 6 +- nugu-ios.xcodeproj/project.pbxproj | 24 +++---- 7 files changed, 83 insertions(+), 61 deletions(-) rename NuguCore/Sources/Network/Api/{ServerSideEventProcessor.swift => ServerSentEventProcessor.swift} (90%) rename NuguCore/Sources/StreamData/{ServerSideEventReceiver.swift => ServerSentEventReceiver.swift} (58%) rename NuguCore/Sources/StreamData/{ServerSideEventReceiverState.swift => ServerSentEventReceiverState.swift} (79%) diff --git a/NuguClientKit/Sources/Client/NuguClientDelegate.swift b/NuguClientKit/Sources/Client/NuguClientDelegate.swift index 762a627f8..a3ec5396f 100644 --- a/NuguClientKit/Sources/Client/NuguClientDelegate.swift +++ b/NuguClientKit/Sources/Client/NuguClientDelegate.swift @@ -27,7 +27,7 @@ public typealias Directive = Downstream.Directive public typealias DirectiveAttachment = Downstream.Attachment public typealias Event = Upstream.Event public typealias EventAttachment = Upstream.Attachment -public typealias ServerSideEventReceiverState = NuguCore.ServerSideEventReceiverState +public typealias ServerSentEventReceiverState = NuguCore.ServerSentEventReceiverState public protocol NuguClientDelegate: AnyObject { // authorization related @@ -80,8 +80,8 @@ public protocol NuguClientDelegate: AnyObject { func nuguClientDidSend(attachment: EventAttachment, error: Error?) /// Notify that nugu client server initiated directive state has been changed - /// - Parameter state: ServerSideEventReceiverState - func nuguClientServerInitiatedDirectiveRecevierStateDidChange(_ state: ServerSideEventReceiverState) + /// - Parameter state: ServerSentEventReceiverState + func nuguClientServerInitiatedDirectiveRecevierStateDidChange(_ state: ServerSentEventReceiverState) func nuguClientRequestRecognitionWithTriggerContext() -> [String: AnyHashable] } @@ -110,6 +110,6 @@ public extension NuguClientDelegate { func nuguClientWillSend(event: Event) {} func nuguClientDidSend(event: Event, error: Error?) {} func nuguClientDidSend(attachment: EventAttachment, error: Error?) {} - func nuguClientServerInitiatedDirectiveRecevierStateDidChange(_ state: ServerSideEventReceiverState) {} + func nuguClientServerInitiatedDirectiveRecevierStateDidChange(_ state: ServerSentEventReceiverState) {} func nuguClientRequestRecognitionWithTriggerContext() -> [String: AnyHashable] { [:] } } diff --git a/NuguCore/Sources/Network/Api/NuguApiProvider.swift b/NuguCore/Sources/Network/Api/NuguApiProvider.swift index 783a4137c..ee0a7f0b1 100644 --- a/NuguCore/Sources/Network/Api/NuguApiProvider.swift +++ b/NuguCore/Sources/Network/Api/NuguApiProvider.swift @@ -56,7 +56,7 @@ class NuguApiProvider: NSObject { @Atomic private var eventResponseProcessors = [URLSessionTask: EventResponseProcessor]() // handle received directives by server side event - private var serverSideEventProcessor: ServerSideEventProcessor? + private var serverSentEventProcessor: ServerSentEventProcessor? // state of client side load balanceing private(set) public var cslbState: ClientSideLoadBalanceState = .unnecessary { @@ -142,8 +142,8 @@ class NuguApiProvider: NSObject { return } - if self.serverSideEventProcessor == nil { - self.serverSideEventProcessor = ServerSideEventProcessor() + if self.serverSentEventProcessor == nil { + self.serverSentEventProcessor = ServerSentEventProcessor() // connect downstream. guard let downstreamUrl = URL(string: NuguApi.directives.uri(baseUrl: loadBalancedUrl)) else { @@ -160,7 +160,7 @@ class NuguApiProvider: NSObject { log.debug("directive request: \(downstreamRequest)\nheader: \(downstreamRequest.allHTTPHeaderFields?.description ?? "")\n") } - single(.success(self.serverSideEventProcessor!.subject)) + single(.success(self.serverSentEventProcessor!.subject)) } return disposable @@ -169,18 +169,18 @@ class NuguApiProvider: NSObject { .flatMap { $0 } .concatMap { [weak self] (data) -> Observable in guard let self = self, - let serverSideEventProcessor = self.serverSideEventProcessor else { + let serverSentEventProcessor = self.serverSentEventProcessor else { return Observable.error(NetworkError.streamInitializeFailed) } - return self.makePart(with: data, processor: serverSideEventProcessor) + return self.makePart(with: data, processor: serverSentEventProcessor) } .do(onError: { error = $0 }, onDispose: { [weak self] in self?.processorQueue.async { self?.cslbState = error == nil ? .unnecessary : .deactivated - self?.serverSideEventProcessor = nil + self?.serverSentEventProcessor = nil task?.cancel() } @@ -367,7 +367,7 @@ extension NuguApiProvider: URLSessionDataDelegate, StreamDelegate { func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { log.debug("didReceive response:\n\(response)\n") - guard let processor: MultiPartProcessable = eventResponseProcessors[dataTask] ?? serverSideEventProcessor else { + guard let processor: MultiPartProcessable = eventResponseProcessors[dataTask] ?? serverSentEventProcessor else { log.error("unknown response: \(response)") completionHandler(.cancel) return @@ -422,17 +422,17 @@ extension NuguApiProvider: URLSessionDataDelegate, StreamDelegate { } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - (eventResponseProcessors[dataTask]?.subject ?? serverSideEventProcessor?.subject)?.onNext(data) + (eventResponseProcessors[dataTask]?.subject ?? serverSentEventProcessor?.subject)?.onNext(data) } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { defer { processorQueue.async { [weak self] in - self?.eventResponseProcessors.keys.contains(task) == true ? (self?.eventResponseProcessors[task] = nil) : (self?.serverSideEventProcessor = nil) + self?.eventResponseProcessors.keys.contains(task) == true ? (self?.eventResponseProcessors[task] = nil) : (self?.serverSentEventProcessor = nil) } } - let processor: MultiPartProcessable? = eventResponseProcessors[task] ?? serverSideEventProcessor + let processor: MultiPartProcessable? = eventResponseProcessors[task] ?? serverSentEventProcessor if let error = error { log.debug("didCompleteWithError: \(error)") processor?.subject.onError(error) diff --git a/NuguCore/Sources/Network/Api/ServerSideEventProcessor.swift b/NuguCore/Sources/Network/Api/ServerSentEventProcessor.swift similarity index 90% rename from NuguCore/Sources/Network/Api/ServerSideEventProcessor.swift rename to NuguCore/Sources/Network/Api/ServerSentEventProcessor.swift index f94e80eae..477d6fd19 100644 --- a/NuguCore/Sources/Network/Api/ServerSideEventProcessor.swift +++ b/NuguCore/Sources/Network/Api/ServerSentEventProcessor.swift @@ -1,5 +1,5 @@ // -// ServerSideEventProcessor.swift +// ServerSentEventProcessor.swift // NuguCore // // Created by childc on 2020/03/04. @@ -22,7 +22,7 @@ import Foundation import RxSwift -class ServerSideEventProcessor: MultiPartProcessable { +class ServerSentEventProcessor: MultiPartProcessable { var parser: MultiPartParser? var data = Data() let subject = PublishSubject() diff --git a/NuguCore/Sources/StreamData/ServerSideEventReceiver.swift b/NuguCore/Sources/StreamData/ServerSentEventReceiver.swift similarity index 58% rename from NuguCore/Sources/StreamData/ServerSideEventReceiver.swift rename to NuguCore/Sources/StreamData/ServerSentEventReceiver.swift index dec92562e..e4b6f88fc 100644 --- a/NuguCore/Sources/StreamData/ServerSideEventReceiver.swift +++ b/NuguCore/Sources/StreamData/ServerSentEventReceiver.swift @@ -1,5 +1,5 @@ // -// ServerSideEventReceiver.swift +// ServerSentEventReceiver.swift // NuguCore // // Created by childc on 2020/03/05. @@ -22,13 +22,14 @@ import Foundation import RxSwift -class ServerSideEventReceiver { +class ServerSentEventReceiver { private let apiProvider: NuguApiProvider private var pingDisposable: Disposable? - private let stateSubject = PublishSubject() + private let stateSubject = PublishSubject() + private let sseStateQueue = DispatchQueue(label: "com.sktelecom.romaine.core.server_sent_event_state") private let disposeBag = DisposeBag() - private(set) var state: ServerSideEventReceiverState = .unconnected { + private(set) var state: ServerSentEventReceiverState = .unconnected { didSet { if oldValue != state { log.debug("server side event receiver state changed from: \(oldValue) to: \(state)") @@ -43,15 +44,19 @@ class ServerSideEventReceiver { } var directive: Observable { - state = .connecting - + sseStateQueue.async { [weak self] in + self?.state = .connecting + } + var error: Error? return apiProvider.directive .enumerated() .map { [weak self] (index: Int, element: MultiPartParser.Part) in - if index == 0 { - // Change state when the first directive arrived - self?.state = .connected + self?.sseStateQueue.async { [weak self] in + if index == .zero { + // Change state when the first directive arrived + self?.state = .connected + } } return element @@ -59,28 +64,36 @@ class ServerSideEventReceiver { .do(onError: { error = $0 }, onDispose: { [weak self] in - if let error = error { - self?.state = .disconnected(error: error) - return + self?.sseStateQueue.async { [weak self] in + if let error = error { + self?.state = .disconnected(error: error) + return + } + + self?.state = .unconnected } - - self?.state = .unconnected }) } - var stateObserver: Observable { + var stateObserver: Observable { return stateSubject } } // MARK: - ping -private extension ServerSideEventReceiver { +private extension ServerSentEventReceiver { + private enum Const { + static let minPingInterval = 180 + static let maxPingInterval = 300 + static let maxRetryCount = 3 + } + func startPing() { - let randomPingTime = Int.random(in: 180..<300) + log.debug("Try to start ping schedule") - pingDisposable?.dispose() - pingDisposable = Observable.interval(.seconds(randomPingTime), scheduler: ConcurrentDispatchQueueScheduler(qos: .default)) + let randomPingTime = Int.random(in: Const.minPingInterval...interval(.seconds(randomPingTime), scheduler: ConcurrentDispatchQueueScheduler(qos: .default)) .flatMap { [weak self] _ -> Completable in guard let apiProvider = self?.apiProvider else { return Completable.error(NetworkError.badRequest) @@ -92,7 +105,7 @@ private extension ServerSideEventReceiver { error .enumerated() .flatMap { (index, error) -> Observable in - guard index < 3 else { + guard index < Const.maxRetryCount else { return Observable.error(error) } @@ -105,14 +118,23 @@ private extension ServerSideEventReceiver { log.debug("Ping schedule for server initiated directive is cancelled") }) - pingDisposable?.disposed(by: disposeBag) - log.debug("Ping schedule for server initiated directive is set. It will be triggered \(randomPingTime) seconds later.") + sseStateQueue.async { [weak self] in + guard let self else { return } + self.pingDisposable?.dispose() + self.pingDisposable = pingDisposable + pingDisposable.disposed(by: disposeBag) + log.debug("Ping schedule for server initiated directive is set. It will be triggered \(randomPingTime) seconds later.") + } } func stopPing() { log.debug("Try to stop ping schedule") - pingDisposable?.dispose() - pingDisposable = nil + sseStateQueue.async { [weak self] in + guard let self else { return } + pingDisposable?.dispose() + pingDisposable = nil + } + } } diff --git a/NuguCore/Sources/StreamData/ServerSideEventReceiverState.swift b/NuguCore/Sources/StreamData/ServerSentEventReceiverState.swift similarity index 79% rename from NuguCore/Sources/StreamData/ServerSideEventReceiverState.swift rename to NuguCore/Sources/StreamData/ServerSentEventReceiverState.swift index e936b1be9..619b45dca 100644 --- a/NuguCore/Sources/StreamData/ServerSideEventReceiverState.swift +++ b/NuguCore/Sources/StreamData/ServerSentEventReceiverState.swift @@ -1,5 +1,5 @@ // -// ServerSideEventReceiverState.swift +// ServerSentEventReceiverState.swift // NuguCore // // Created by childc on 2020/03/10. @@ -22,8 +22,8 @@ import Foundation import NuguUtils -public enum ServerSideEventReceiverState: Equatable, EnumTypedNotification { - public static var name: Notification.Name = .serverSideEventReceiverStateDidChange +public enum ServerSentEventReceiverState: Equatable, EnumTypedNotification { + public static var name: Notification.Name = .serverSentEventReceiverStateDidChange /// Didn't try to connect to server or connection reset case unconnected @@ -38,7 +38,7 @@ public enum ServerSideEventReceiverState: Equatable, EnumTypedNotification { /// - Parameter error: If Connection closed because of the error. case disconnected(error: Error) - public static func == (lhs: ServerSideEventReceiverState, rhs: ServerSideEventReceiverState) -> Bool { + public static func == (lhs: ServerSentEventReceiverState, rhs: ServerSentEventReceiverState) -> Bool { switch (lhs, rhs) { case (.unconnected, .unconnected), (.connected, .connected), @@ -55,5 +55,5 @@ public enum ServerSideEventReceiverState: Equatable, EnumTypedNotification { } extension Notification.Name { - static let serverSideEventReceiverStateDidChange = Notification.Name("com.sktelecom.romaine.notification.name.server_side_event_receiver_state_did_change") + static let serverSentEventReceiverStateDidChange = Notification.Name("com.sktelecom.romaine.notification.name.server_sent_event_receiver_state_did_change") } diff --git a/NuguCore/Sources/StreamData/StreamDataRouter.swift b/NuguCore/Sources/StreamData/StreamDataRouter.swift index 2c86a05ea..e8abb370e 100644 --- a/NuguCore/Sources/StreamData/StreamDataRouter.swift +++ b/NuguCore/Sources/StreamData/StreamDataRouter.swift @@ -31,14 +31,14 @@ public class StreamDataRouter: StreamDataRoutable { private let directiveSequencer: DirectiveSequenceable @Atomic private var eventSenders = [String: EventSender]() @Atomic private var eventDisposables = [String: Disposable]() - private var serverInitiatedDirectiveReceiver: ServerSideEventReceiver + private var serverInitiatedDirectiveReceiver: ServerSentEventReceiver private var serverInitiatedDirectiveCompletion: ((StreamDataState) -> Void)? private var serverInitiatedDirectiveDisposable: Disposable? private var serverInitiatedDirectiveStateDisposable: Disposable? private let disposeBag = DisposeBag() public init(directiveSequencer: DirectiveSequenceable) { - serverInitiatedDirectiveReceiver = ServerSideEventReceiver(apiProvider: nuguApiProvider) + serverInitiatedDirectiveReceiver = ServerSentEventReceiver(apiProvider: nuguApiProvider) self.directiveSequencer = directiveSequencer } } @@ -408,6 +408,6 @@ public extension NuguCoreNotification { } } - public typealias ServerInitiatedDirectiveReceiverState = ServerSideEventReceiverState + public typealias ServerInitiatedDirectiveReceiverState = ServerSentEventReceiverState } } diff --git a/nugu-ios.xcodeproj/project.pbxproj b/nugu-ios.xcodeproj/project.pbxproj index 09e5543ec..53e252013 100644 --- a/nugu-ios.xcodeproj/project.pbxproj +++ b/nugu-ios.xcodeproj/project.pbxproj @@ -270,7 +270,7 @@ 7345DFF325C68B3A006DBCC6 /* DataBoundInputStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7345DFF225C68B3A006DBCC6 /* DataBoundInputStream.swift */; }; 7352F19A2A37225600B0199C /* UIImage+resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7352F1992A37225600B0199C /* UIImage+resize.swift */; }; 735A4CBE241172F1004E7A41 /* EventResponseProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735A4CBA241172F0004E7A41 /* EventResponseProcessor.swift */; }; - 735A4CBF241172F1004E7A41 /* ServerSideEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735A4CBB241172F0004E7A41 /* ServerSideEventProcessor.swift */; }; + 735A4CBF241172F1004E7A41 /* ServerSentEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735A4CBB241172F0004E7A41 /* ServerSentEventProcessor.swift */; }; 735A4CC0241172F1004E7A41 /* NuguApiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735A4CBC241172F0004E7A41 /* NuguApiProvider.swift */; }; 735A4CC1241172F1004E7A41 /* NuguApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735A4CBD241172F1004E7A41 /* NuguApi.swift */; }; 735A4CC624117339004E7A41 /* MultiPartParserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735A4CC424117338004E7A41 /* MultiPartParserError.swift */; }; @@ -361,8 +361,8 @@ 7378FDD925B8191200AB9764 /* TypedNotifyable+post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7378FDD825B8191200AB9764 /* TypedNotifyable+post.swift */; }; 738224BE26D4DB5D0050D67A /* DataStreamPlayerBufferState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738224BD26D4DB5D0050D67A /* DataStreamPlayerBufferState.swift */; }; 7386DA9923CF279C002BF24C /* NuguClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7386DA9823CF279C002BF24C /* NuguClientDelegate.swift */; }; - 739078FD241A3E0C007D753F /* ServerSideEventReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739078FB241A3E0B007D753F /* ServerSideEventReceiver.swift */; }; - 739078FE241A3E0C007D753F /* ServerSideEventReceiverState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739078FC241A3E0C007D753F /* ServerSideEventReceiverState.swift */; }; + 739078FE241A3E0C007D753F /* ServerSentEventReceiverState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739078FC241A3E0C007D753F /* ServerSentEventReceiverState.swift */; }; + 73A76A5A2B6B942C007F4178 /* ServerSentEventReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A76A592B6B942C007F4178 /* ServerSentEventReceiver.swift */; }; 73A84E3B2799990200133D45 /* RxSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73C256AA269740BB0008FE7F /* RxSwift.xcframework */; }; 73A84E3C2799990200133D45 /* RxSwift.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 73C256AA269740BB0008FE7F /* RxSwift.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 73BF06DE26A0208700112473 /* NuguObjcUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 73BF06DC26A0208700112473 /* NuguObjcUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1089,7 +1089,7 @@ 7345DFF225C68B3A006DBCC6 /* DataBoundInputStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataBoundInputStream.swift; sourceTree = ""; }; 7352F1992A37225600B0199C /* UIImage+resize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+resize.swift"; sourceTree = ""; }; 735A4CBA241172F0004E7A41 /* EventResponseProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventResponseProcessor.swift; sourceTree = ""; }; - 735A4CBB241172F0004E7A41 /* ServerSideEventProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerSideEventProcessor.swift; sourceTree = ""; }; + 735A4CBB241172F0004E7A41 /* ServerSentEventProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerSentEventProcessor.swift; sourceTree = ""; }; 735A4CBC241172F0004E7A41 /* NuguApiProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NuguApiProvider.swift; sourceTree = ""; }; 735A4CBD241172F1004E7A41 /* NuguApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NuguApi.swift; sourceTree = ""; }; 735A4CC424117338004E7A41 /* MultiPartParserError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiPartParserError.swift; sourceTree = ""; }; @@ -1176,8 +1176,8 @@ 7378FDD825B8191200AB9764 /* TypedNotifyable+post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TypedNotifyable+post.swift"; sourceTree = ""; }; 738224BD26D4DB5D0050D67A /* DataStreamPlayerBufferState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStreamPlayerBufferState.swift; sourceTree = ""; }; 7386DA9823CF279C002BF24C /* NuguClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NuguClientDelegate.swift; sourceTree = ""; }; - 739078FB241A3E0B007D753F /* ServerSideEventReceiver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerSideEventReceiver.swift; sourceTree = ""; }; - 739078FC241A3E0C007D753F /* ServerSideEventReceiverState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerSideEventReceiverState.swift; sourceTree = ""; }; + 739078FC241A3E0C007D753F /* ServerSentEventReceiverState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerSentEventReceiverState.swift; sourceTree = ""; }; + 73A76A592B6B942C007F4178 /* ServerSentEventReceiver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ServerSentEventReceiver.swift; path = NuguCore/Sources/StreamData/ServerSentEventReceiver.swift; sourceTree = SOURCE_ROOT; }; 73BF06DA26A0208700112473 /* NuguObjcUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NuguObjcUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 73BF06DC26A0208700112473 /* NuguObjcUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NuguObjcUtils.h; sourceTree = ""; }; 73BF06DD26A0208700112473 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -2770,7 +2770,7 @@ 735A4CBA241172F0004E7A41 /* EventResponseProcessor.swift */, 735A4CBD241172F1004E7A41 /* NuguApi.swift */, 735A4CBC241172F0004E7A41 /* NuguApiProvider.swift */, - 735A4CBB241172F0004E7A41 /* ServerSideEventProcessor.swift */, + 735A4CBB241172F0004E7A41 /* ServerSentEventProcessor.swift */, ); name = Api; path = ../Api; @@ -2792,8 +2792,7 @@ 7373892924A46D420018DDD2 /* StreamDataRoutable.swift */, 7373892824A46D420018DDD2 /* StreamDataState.swift */, 7373892B24A46D420018DDD2 /* UpstreamDataSendable.swift */, - 739078FB241A3E0B007D753F /* ServerSideEventReceiver.swift */, - 739078FC241A3E0C007D753F /* ServerSideEventReceiverState.swift */, + 739078FC241A3E0C007D753F /* ServerSentEventReceiverState.swift */, 7EDCF2A62384FE88006F96B6 /* StreamDataRouter.swift */, 735A4CDF241173F4004E7A41 /* EventSender.swift */, 735A4CE0241173F4004E7A41 /* EventSenderError.swift */, @@ -2813,6 +2812,7 @@ 7373893024A46D4D0018DDD2 /* Model */ = { isa = PBXGroup; children = ( + 73A76A592B6B942C007F4178 /* ServerSentEventReceiver.swift */, 7373893124A46D4D0018DDD2 /* Upstream.swift */, 7373893224A46D4D0018DDD2 /* Downstream.swift */, ); @@ -4085,7 +4085,7 @@ 75082BC6243AC85E0027D76D /* FileManager+Convenience.swift in Sources */, 7373895724A473A60018DDD2 /* FocusChannelPriority.swift in Sources */, 7373894424A46E5F0018DDD2 /* ContextType.swift in Sources */, - 739078FE241A3E0C007D753F /* ServerSideEventReceiverState.swift in Sources */, + 739078FE241A3E0C007D753F /* ServerSentEventReceiverState.swift in Sources */, 7315301923E11EDF00F843C3 /* MediaPlayableError.swift in Sources */, 7373892124A46D120018DDD2 /* MediaPlayerDelegate.swift in Sources */, 7373895824A473A60018DDD2 /* FocusChannelDelegate.swift in Sources */, @@ -4103,6 +4103,7 @@ 1FFFF3C12375707100C9A177 /* Policy.swift in Sources */, 1FFFF3C82375707100C9A177 /* FocusConst.swift in Sources */, 7373895924A473A60018DDD2 /* FocusManageable.swift in Sources */, + 73A76A5A2B6B942C007F4178 /* ServerSentEventReceiver.swift in Sources */, 7373892C24A46D430018DDD2 /* StreamDataState.swift in Sources */, 7373894224A46E5F0018DDD2 /* ContextInfo.swift in Sources */, 73454FC32387BDF00073AF48 /* NuguServerInfo.swift in Sources */, @@ -4141,8 +4142,7 @@ 7373894A24A46E720018DDD2 /* BlockingPolicy.swift in Sources */, 1FFFF3C72375707100C9A177 /* FocusManager.swift in Sources */, 7373893324A46D4D0018DDD2 /* Upstream.swift in Sources */, - 739078FD241A3E0C007D753F /* ServerSideEventReceiver.swift in Sources */, - 735A4CBF241172F1004E7A41 /* ServerSideEventProcessor.swift in Sources */, + 735A4CBF241172F1004E7A41 /* ServerSentEventProcessor.swift in Sources */, 7EDCF2A72384FE88006F96B6 /* StreamDataRouter.swift in Sources */, 735A4CE1241173F4004E7A41 /* EventSender.swift in Sources */, 7373894B24A46E720018DDD2 /* DirectiveHandleInfo.swift in Sources */, From 0f15a76fb22cd1f4b01b279aebf2e2fddab895a4 Mon Sep 17 00:00:00 2001 From: Sokiwar Date: Tue, 20 Feb 2024 16:31:49 +0900 Subject: [PATCH 20/24] =?UTF-8?q?ServerSentEventReceiver=20=ED=81=AC?= =?UTF-8?q?=EB=9E=98=EC=8B=9C=20=EC=88=98=EC=A0=95=20(#1130)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - ServerSentEventReceiver 크래시 수정 - Range범위 수정 --- NuguCore/Sources/StreamData/ServerSentEventReceiver.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuguCore/Sources/StreamData/ServerSentEventReceiver.swift b/NuguCore/Sources/StreamData/ServerSentEventReceiver.swift index e4b6f88fc..57e5b4648 100644 --- a/NuguCore/Sources/StreamData/ServerSentEventReceiver.swift +++ b/NuguCore/Sources/StreamData/ServerSentEventReceiver.swift @@ -92,7 +92,7 @@ private extension ServerSentEventReceiver { func startPing() { log.debug("Try to start ping schedule") - let randomPingTime = Int.random(in: Const.minPingInterval...interval(.seconds(randomPingTime), scheduler: ConcurrentDispatchQueueScheduler(qos: .default)) .flatMap { [weak self] _ -> Completable in guard let apiProvider = self?.apiProvider else { From e841ac9795ecefbe612e8c0f76cc9868f18c75fc Mon Sep 17 00:00:00 2001 From: Sokiwar Date: Wed, 21 Feb 2024 11:45:49 +0900 Subject: [PATCH 21/24] =?UTF-8?q?ControlCenter=EC=97=90=EC=84=9C=20offset?= =?UTF-8?q?=EB=93=9C=EB=9E=98=EA=B7=B8=EC=8B=9C=20NowPlayingInfo=EC=9D=98?= =?UTF-8?q?=20offset=EB=8F=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#1131)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - ControlCenter에서 offset드래그시 NowPlayingInfo의 offset도 업데이트하도록 수정 - 드래그한것은 반영이 되나, 실제로 값이 업데이트는 되지 않아 값을 업데이트해주도록 변경 --- NuguClientKit/Sources/Audio/ControlCenterManager.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/NuguClientKit/Sources/Audio/ControlCenterManager.swift b/NuguClientKit/Sources/Audio/ControlCenterManager.swift index 079161bd3..26bc3f570 100644 --- a/NuguClientKit/Sources/Audio/ControlCenterManager.swift +++ b/NuguClientKit/Sources/Audio/ControlCenterManager.swift @@ -286,6 +286,7 @@ private extension ControlCenterManager { .changePlaybackPositionCommand.addTarget { [weak self] (event) -> MPRemoteCommandHandlerStatus in guard let event = event as? MPChangePlaybackPositionCommandEvent else { return .commandFailed } self?.audioPlayerAgent.seek(to: Int(event.positionTime)) + self?.update(offset: Int(event.positionTime)) return .success } } From a223ac1bb55fe0c78848098ce9fe4212b3daab06 Mon Sep 17 00:00:00 2001 From: Sokiwar Date: Mon, 4 Mar 2024 15:47:00 +0900 Subject: [PATCH 22/24] AudioPlayer Version Up(1.8 -> 1.9) (#1133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - AudioPlayer Template 필드 업데이트에 따른 Capability Version 업데이트 - libararyAvailable 필드 추가 --- .../CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift | 2 +- .../AudioPlayer/Display/Models/AudioPlayerPlaylist.swift | 5 ++++- .../Display/Models/AudioPlayerSettingsTemplate.swift | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift index 8ba21f151..3e91f4127 100644 --- a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift @@ -27,7 +27,7 @@ import RxSwift public final class AudioPlayerAgent: AudioPlayerAgentProtocol { // CapabilityAgentable - public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .audioPlayer, version: "1.8") + public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .audioPlayer, version: "1.9") private let playSyncProperty = PlaySyncProperty(layerType: .media, contextType: .sound) // AudioPlayerAgentProtocol diff --git a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerPlaylist.swift b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerPlaylist.swift index 0e855a2aa..b5ef9c506 100644 --- a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerPlaylist.swift +++ b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerPlaylist.swift @@ -68,7 +68,7 @@ public struct AudioPlayerPlaylist: Codable { } private enum CodingKeys: String, CodingKey { - case text = "text" + case text case subText case imageUrl case badgeUrl @@ -77,6 +77,7 @@ public struct AudioPlayerPlaylist: Codable { case token case postback case favorite + case libraryAvailable } public let text: TextObject? @@ -88,6 +89,7 @@ public struct AudioPlayerPlaylist: Codable { public let token: String public let postback: [String: AnyHashable]? public var favorite: Favorite? + public let libraryAvailable: Bool? public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) @@ -100,6 +102,7 @@ public struct AudioPlayerPlaylist: Codable { token = try container.decode(String.self, forKey: .token) postback = try container.decodeIfPresent([String: AnyHashable].self, forKey: .postback) favorite = try container.decodeIfPresent(Favorite.self, forKey: .favorite) + libraryAvailable = try container.decodeIfPresent(Bool.self, forKey: .libraryAvailable) } public func encode(to encoder: Encoder) throws { diff --git a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerSettingsTemplate.swift b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerSettingsTemplate.swift index edc2b9fd8..186a0f372 100644 --- a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerSettingsTemplate.swift +++ b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/Display/Models/AudioPlayerSettingsTemplate.swift @@ -21,5 +21,6 @@ public struct AudioPlayerSettingsTemplate: Decodable { public let favorite: Bool? public let `repeat`: AudioPlayerDisplayRepeat? + public let libraryAvailable: Bool? public let shuffle: Bool? } From bf9d02e7c89e874bae866a9abc22bc63277aed19 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 5 Mar 2024 07:31:30 +0900 Subject: [PATCH 23/24] =?UTF-8?q?AudioBuffer=EB=A5=BC=20speex=20encoder?= =?UTF-8?q?=EB=A1=9C=20=EC=A7=81=EC=A0=91=20=EC=9D=B8=EC=BD=94=EB=94=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20(#1132)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - AudioBuffer를 speex encoder로 직접 인코딩하도록 변경 --- .../Sources/TycheEndPointDetectorEngine.swift | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/JadeMarble/Sources/TycheEndPointDetectorEngine.swift b/JadeMarble/Sources/TycheEndPointDetectorEngine.swift index 5f0a1a3e3..20b7f20a0 100644 --- a/JadeMarble/Sources/TycheEndPointDetectorEngine.swift +++ b/JadeMarble/Sources/TycheEndPointDetectorEngine.swift @@ -28,6 +28,7 @@ public class TycheEndPointDetectorEngine { private var flushedLength: Int = 0 private var flushLength: Int = 0 private var engineHandle: EpdHandle? + private var speexEncoder: SpeexEncoder? public weak var delegate: TycheEndPointDetectorEngineDelegate? #if DEBUG @@ -97,44 +98,46 @@ public class TycheEndPointDetectorEngine { return } - let engineState = ptrPcmData.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.frameLength*2)) { (ptrData) -> Int32 in + let (engineState, inputData) = ptrPcmData.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.frameLength * 2)) { (ptrData) -> (Int32, Data) in #if DEBUG - self.inputData.append(ptrData, count: Int(buffer.frameLength)*2) + self.inputData.append(ptrData, count: Int(buffer.frameLength) * 2) #endif + let inputData = Data(bytes: ptrData, count: Int(buffer.frameLength) * 2) // Calculate flushed audio frame length. var adjustLength = 0 if self.flushedLength + Int(buffer.frameLength) <= self.flushLength { self.flushedLength += Int(buffer.frameLength) - return -1 + return (-1, inputData) } else if self.flushedLength < self.flushLength { self.flushedLength += Int(buffer.frameLength) adjustLength = Int(buffer.frameLength) - (self.flushedLength - self.flushLength) } - return epdClientChannelRUN( + let engineState = epdClientChannelRUN( self.engineHandle, ptrData, - myint(UInt32(buffer.frameLength) - UInt32(adjustLength))*2, // data length is double of frame length, because It is 16bit audio data. + myint(UInt32(buffer.frameLength) - UInt32(adjustLength)) * 2, // data length is double of frame length, because It is 16bit audio data. 0 ) + + return (engineState, inputData) } - guard 0 <= engineState else { return } + guard .zero <= engineState else { return } - let length = epdClientChannelGetOutputDataSize(self.engineHandle) - if 0 < length { - let detectedBytes = UnsafeMutablePointer.allocate(capacity: Int(length)) - defer { detectedBytes.deallocate() } - - let result = epdClientChannelGetOutputData(self.engineHandle, detectedBytes, length) - if 0 < result { - let detectedData = Data(bytes: detectedBytes, count: Int(result)) - self.delegate?.tycheEndPointDetectorEngineDidExtract(speechData: detectedData) - - #if DEBUG - self.outputData.append(detectedData) - #endif - } + guard let speexEncoder else { + log.error("SpeexEncoder is not exist. Please initDetectorEngine first.") + return + } + + do { + let speexData = try speexEncoder.encode(data: inputData) + self.delegate?.tycheEndPointDetectorEngineDidExtract(speechData: speexData) + #if DEBUG + self.outputData.append(speexData) + #endif + } catch { + log.error("Failed to speex encoding, error: \(error)") } self.state = TycheEndPointDetectorEngine.State(engineState: engineState) @@ -181,6 +184,7 @@ public class TycheEndPointDetectorEngine { log.debug("engine is destroyed") } + speexEncoder = nil state = .idle } @@ -200,6 +204,8 @@ public class TycheEndPointDetectorEngine { let modelPath = Bundle.module.url(forResource: "skt_epd_model", withExtension: "raw")!.path #endif + let speexEncoder = SpeexEncoder(sampleRate: Int(sampleRate), inputType: EndPointDetectorConst.inputStreamType) + self.speexEncoder = speexEncoder guard let epdHandle = epdClientChannelSTART( modelPath, myint(sampleRate), From 65c8f0a4ce2fa7a4e822aa2efd50d0e0960b26b6 Mon Sep 17 00:00:00 2001 From: jayce1116 Date: Tue, 12 Mar 2024 07:23:03 +0900 Subject: [PATCH 24/24] update version to `1.9.3` (#1139) ### Description - update version to `1.9.3` --- JadeMarble.podspec | 2 +- KeenSense.podspec | 2 +- NuguAgents.podspec | 2 +- NuguClientKit.podspec | 2 +- NuguCore.podspec | 2 +- NuguCore/Sources/NuguCore.swift | 2 +- NuguLoginKit.podspec | 2 +- NuguObjcUtils.podspec | 2 +- NuguServiceKit.podspec | 2 +- NuguUIKit.podspec | 2 +- NuguUtils.podspec | 2 +- OpusSDK.podspec | 2 +- SilverTray.podspec | 2 +- TycheSDK.podspec | 2 +- nugu-ios.xcodeproj/project.pbxproj | 56 +++++++++++++++--------------- 15 files changed, 42 insertions(+), 42 deletions(-) diff --git a/JadeMarble.podspec b/JadeMarble.podspec index 95635e41f..fd90340e0 100644 --- a/JadeMarble.podspec +++ b/JadeMarble.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'JadeMarble' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'End Point Detector for NUGU ASR' s.homepage = 'https://github.com/nugu-developers/nugu-ios' diff --git a/KeenSense.podspec b/KeenSense.podspec index e716c0524..286cd2a00 100644 --- a/KeenSense.podspec +++ b/KeenSense.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KeenSense' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Key Word Detector for NUGU' s.homepage = 'https://github.com/nugu-developers/nugu-ios' diff --git a/NuguAgents.podspec b/NuguAgents.podspec index 79e61f81c..0a31ac534 100644 --- a/NuguAgents.podspec +++ b/NuguAgents.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguAgents' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Nugu Agents' s.description = <<-DESC diff --git a/NuguClientKit.podspec b/NuguClientKit.podspec index ab9f9aef9..aa3b2cd18 100644 --- a/NuguClientKit.podspec +++ b/NuguClientKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguClientKit' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Nugu Client Kit' s.description = <<-DESC diff --git a/NuguCore.podspec b/NuguCore.podspec index 18a43822b..bc8fba8b9 100644 --- a/NuguCore.podspec +++ b/NuguCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguCore' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Nugu' s.description = <<-DESC diff --git a/NuguCore/Sources/NuguCore.swift b/NuguCore/Sources/NuguCore.swift index 8cf7d15cb..0d0330601 100644 --- a/NuguCore/Sources/NuguCore.swift +++ b/NuguCore/Sources/NuguCore.swift @@ -56,5 +56,5 @@ public var logLevel: NattyLog.LogLevel { public let nuguSDKVersion = (Bundle(for: AuthorizationStore.self).object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0") #else // FIXME: 현재는 SPM에서 버전을 가져올 방법이 없다. -public let nuguSDKVersion = "1.9.2" +public let nuguSDKVersion = "1.9.3" #endif diff --git a/NuguLoginKit.podspec b/NuguLoginKit.podspec index 3df409916..5bb262fef 100644 --- a/NuguLoginKit.podspec +++ b/NuguLoginKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguLoginKit' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Supported login for Nugu Service' s.description = <<-DESC diff --git a/NuguObjcUtils.podspec b/NuguObjcUtils.podspec index f6ea537f7..36fa915ec 100644 --- a/NuguObjcUtils.podspec +++ b/NuguObjcUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguObjcUtils' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Nugu Utils' s.description = <<-DESC diff --git a/NuguServiceKit.podspec b/NuguServiceKit.podspec index c2edc87c4..534d21a44 100644 --- a/NuguServiceKit.podspec +++ b/NuguServiceKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguServiceKit' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Customized Webview for Nugu Service' s.description = <<-DESC diff --git a/NuguUIKit.podspec b/NuguUIKit.podspec index 6fb7a1eb4..58006a041 100644 --- a/NuguUIKit.podspec +++ b/NuguUIKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguUIKit' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'UI Set of Nugu Service' s.description = <<-DESC diff --git a/NuguUtils.podspec b/NuguUtils.podspec index 502775333..cea1821c4 100644 --- a/NuguUtils.podspec +++ b/NuguUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NuguUtils' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Supported login for Nugu Service' s.description = <<-DESC diff --git a/OpusSDK.podspec b/OpusSDK.podspec index 6cedddaa5..27dd4e4c1 100644 --- a/OpusSDK.podspec +++ b/OpusSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'OpusSDK' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Opus codec wrapper' s.description = <<-DESC diff --git a/SilverTray.podspec b/SilverTray.podspec index f893d5a88..1e5f4b501 100644 --- a/SilverTray.podspec +++ b/SilverTray.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SilverTray' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Data chunk player' s.description = <<-DESC diff --git a/TycheSDK.podspec b/TycheSDK.podspec index 778503dad..56846f4ed 100644 --- a/TycheSDK.podspec +++ b/TycheSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TycheSDK' - s.version = '1.9.2' + s.version = '1.9.3' s.license = 'Apache License, Version 2.0' s.summary = 'Tyche wrapper' s.description = <<-DESC diff --git a/nugu-ios.xcodeproj/project.pbxproj b/nugu-ios.xcodeproj/project.pbxproj index 53e252013..b57955f23 100644 --- a/nugu-ios.xcodeproj/project.pbxproj +++ b/nugu-ios.xcodeproj/project.pbxproj @@ -4795,7 +4795,7 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguCore; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4832,7 +4832,7 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguCore; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4865,7 +4865,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguClientKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4898,7 +4898,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguClientKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4931,7 +4931,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguLoginKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4964,7 +4964,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguLoginKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4997,7 +4997,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; OTHER_SWIFT_FLAGS = "-DDEPLOY_OTHER_PACKAGE_MANAGER"; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguUIKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -5031,7 +5031,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; OTHER_SWIFT_FLAGS = "-DDEPLOY_OTHER_PACKAGE_MANAGER"; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguUIKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -5066,7 +5066,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; OTHER_LDFLAGS = ( "$(inherited)", "-l\"c++\"", @@ -5106,7 +5106,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; OTHER_LDFLAGS = ( "$(inherited)", "-l\"c++\"", @@ -5145,7 +5145,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; OTHER_LDFLAGS = ( "$(inherited)", "-l\"c++\"", @@ -5184,7 +5184,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; OTHER_LDFLAGS = ( "$(inherited)", "-l\"c++\"", @@ -5223,7 +5223,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.SampleApp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = NuguSampleAppDev; @@ -5254,7 +5254,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.SampleApp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = NuguSampleAppDev; @@ -5414,7 +5414,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguUtils; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -5447,7 +5447,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguUtils; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -5480,7 +5480,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguAgents; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5512,7 +5512,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguAgents; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5542,7 +5542,7 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = ""; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.TycheSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5570,7 +5570,7 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = ""; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.TycheSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5596,7 +5596,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.OpusSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5622,7 +5622,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.OpusSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5649,7 +5649,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguObjcUtils; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5678,7 +5678,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguObjcUtils; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5710,7 +5710,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.SilverTray; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5742,7 +5742,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.SilverTray; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -5775,7 +5775,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguServiceKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -5810,7 +5810,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; PRODUCT_BUNDLE_IDENTIFIER = com.sktelecom.romaine.NuguServiceKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = "";