Nano Hash - криптовалюты, майнинг, программирование

Как передавать аудио в v20160207 Alexa Voice Service API в iOS

Я некоторое время работаю над приложением Alexa для iOS, но у меня возникают проблемы с отправкой звука с микрофона в виде потока в AVS API.

Мне удалось предварительно записать аудиосэмпл, отправить его целиком и получить ответ.

Я просто хочу знать, как я могу передавать данные в AVS с помощью соединения NSURLSession http/2.

Вот фрагмент кода, что я делаю сейчас:

func sendData() {
        let request = NSMutableURLRequest(URL: NSURL(string: "https://avs-alexa-na.amazon.com/v20160207/events")!)
        request.setValue("Bearer \(Settings.Credentials.TOKEN)", forHTTPHeaderField: "authorization")
        request.HTTPMethod = "POST"

        let boundry = NSUUID().UUIDString
        let contentType = "multipart/form-data; boundary=\(boundry)"
        request.setValue(contentType, forHTTPHeaderField: "content-type")

        let bodyData = NSMutableData()

        let jsonData = "{\"context\":[{\"header\":{\"namespace\":\"Alerts\",\"name\":\"AlertsState\"},\"payload\":{\"allAlerts\":[],\"activeAlerts\":[]}},{\"header\":{\"namespace\":\"AudioPlayer\",\"name\":\"PlaybackState\"},\"payload\":{\"token\":\"\",\"offsetInMilliseconds\":0,\"playerActivity\":\"IDLE\"}},{\"header\":{\"namespace\":\"Speaker\",\"name\":\"VolumeState\"},\"payload\":{\"volume\":25,\"muted\":false}},{\"header\":{\"namespace\":\"SpeechSynthesizer\",\"name\":\"SpeechState\"},\"payload\":{\"token\":\"\",\"offsetInMilliseconds\":0,\"playerActivity\":\"FINISHED\"}}],\"event\":{\"header\":{\"namespace\":\"SpeechRecognizer\",\"name\":\"Recognize\",\"messageId\":\"messageId-123\",\"dialogRequestId\":\"dialogRequestId-321\"},\"payload\":{\"profile\":\"CLOSE_TALK\",\"format\":\"AUDIO_L16_RATE_16000_CHANNELS_1\"}}}"

        bodyData.appendData("--\(boundry)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        bodyData.appendData("Content-Disposition: form-data; name=\"metadata\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        bodyData.appendData("Content-Type: application/json; charset=UTF-8\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        bodyData.appendData(jsonData.dataUsingEncoding(NSUTF8StringEncoding)!)
        bodyData.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

        bodyData.appendData("--\(boundry)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

        bodyData.appendData("Content-Disposition: form-data; name=\"audio\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        //        bodyData.appendData("Content-Type: audio/L16; rate=16000; channels=1\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        bodyData.appendData("Content-Type: application/octet-stream\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        bodyData.appendData(audioData!)
        bodyData.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

        bodyData.appendData("--\(boundry)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
        session = NSURLSession.sharedSession()
        session.configuration.timeoutIntervalForResource = 60000
        session.configuration.timeoutIntervalForRequest = 60000

        let upload = session.uploadTaskWithRequest(request, fromData: bodyData) { (data, response, error) in
            print("done")
            if(data?.length > 0) {
                print("break")
            }
            if let httpResponse = response as? NSHTTPURLResponse {
                if let responseData = data, let contentTypeHeader = httpResponse.allHeaderFields["Content-Type"] {

                    var boundry: String?
                    let ctbRange = contentTypeHeader.rangeOfString("boundary=.*?;", options: .RegularExpressionSearch)
                    if ctbRange.location != NSNotFound {
                        let boundryNSS = contentTypeHeader.substringWithRange(ctbRange) as NSString
                        boundry = boundryNSS.substringWithRange(NSRange(location: 9, length: boundryNSS.length - 10))
                    }

                    if let b = boundry {
                        let parts = self.parseResponse(responseData, boundry: b)
                        print("got parts")
//                        self.sendSynchronize()
                        self.successHandler?(data: responseData, parts:self.parseResponse(responseData, boundry: b))
                    } else {
                        print("something went wrong")
                        self.errorHandler?(error: NSError(domain: Settings.Error.ErrorDomain, code: Settings.Error.AVSResponseBorderParseErrorCode, userInfo: [NSLocalizedDescriptionKey : "Could not find boundry in AVS response"]))
                    }
                }
            }
        }

        upload.resume()
    }

Эта функция вызывается через каждые 320 байт аудиоданных, потому что Amazon рекомендует именно такой размер для потоковой передачи :)

Приветствую!


  • Удачи @tomwyckhuys? Меня тоже поразило в том же вопросе. Я также попытался удалить термин конечной границы. 01.06.2017
  • Привет, проверьте этот код github.com/grimlockrocks/alexa-swift3-sample-app 25.05.2018

Ответы:


1

Вам следует отправлять только заголовки метаданных JSON. один раз, в начале диалогового запроса (например, в тот момент, когда микрофон открыт и начинается запись).

Вы также захотите использовать одно и то же граничное значение каждый раз, когда вы вызываете метод sendData для одного и того же потока. Используйте один и тот же поток HTTP/2 для всего запроса, а это означает, что вам потребуется реорганизовать метод sendData «наизнанку», чтобы приспособиться к этому. Примеры, в которых используется uploadTask:withStreamedRequest, могут быть полезны (которые вам, вероятно, придется использовать).

Я не знаком с API-интерфейсами Swift HTTP/2, поэтому я не знаю, будут ли обрабатываться кадры продолжения для вас, или вам нужно будет управлять этим самостоятельно, так что на это стоит обратить внимание. Удачи и надеюсь, что это поможет.

26.08.2017

2

как это:

public func send(event: AlexaEvent?) {
        self.queue.async {[weak self] in
            guard let self = self else { return }
            let urlStr = self.host.appending(AlexaConstant.ServiceUrl.eventsURL)
            var eventRequest: URLRequest = URLRequest(url: URL(string: urlStr)!)
            eventRequest.httpMethod = "POST"
            eventRequest.setValue("multipart/form-data; boundary=\(AlexaConstant.HttpBodyData.boundary)", forHTTPHeaderField: "Content-Type")
            self.addAuthHeader(request: &eventRequest)
            guard let bodyData = event?.HTTPBodyData else { return }
            eventRequest.httpBody = bodyData
//            self.bodyStream = InputStream(data: bodyData)
            eventRequest.httpBodyStream = self.bodyStream

            let uploadTask = self.session?.uploadTask(withStreamedRequest: eventRequest)
            guard let task = uploadTask else { return }
            self.state = .started(.init(task: task))
            task.resume()
        }
    }

Затем входные и выходные потоки связываются в прокси-методе URLSessionDataDelegate:

func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
        let sendTimer = Timer(timeInterval: TimeInterval(1), target: self, selector: #selector(didFire(sendTimer:)), userInfo: nil, repeats: true)

        let streamingState = StreamingState(task: task, sendTimer: sendTimer)
        self.state = .streaming(streamingState)
        var bodyData = Data()
        let data = AlexaHttpBodyData.jsonContent()
        guard let jsonObj = try? JSONSerialization.jsonObject(with: data, options: []) else { return }
        guard let valueData = try? JSONSerialization.data(withJSONObject: jsonObj, options: []) else { return }
        bodyData.append(AlexaHttpBodyData.boundaryBegin)
        bodyData.append(AlexaHttpBodyData.jsonHeaders)
        bodyData.append(AlexaHttpBodyData.jsonContent(data: valueData))
        bodyData.append(AlexaHttpBodyData.boundaryBegin)
        bodyData.append(AlexaHttpBodyData.AudioHeaders)
//        bodyStream = InputStream(data: bodyData)
        let streams = Stream.boundPair(bufferSize: BufferSize, inputStream: bodyStream)
        self.bodyStream = streams.inputStream
        self.outputStream = streams.outputStream
//        RunLoop.current.add(streamingState.sendTimer, forMode: .default)
        outputStream?.delegate = self
        outputStream?.schedule(in: .current, forMode: .default)
        outputStream?.open()
        completionHandler(self.bodyStream)
        outputStream?.write(Array(bodyData), maxLength: bodyData.count)
//        while (true) {
        outputStream?.write(Array(self.audioQueue.dequeue()), maxLength: BufferSize)
        let rndata = "\r\n".data(using: .utf8)
        outputStream?.write(Array(rndata!), maxLength: rndata!.count)
        outputStream?.write(Array(AlexaHttpBodyData.boundaryEnd), maxLength: AlexaHttpBodyData.boundaryEnd.count)

//        }
        stop(error: nil)
    }
extension Stream {
static func boundPair(bufferSize: Int, inputStream: InputStream?) -> (inputStream: InputStream?, outputStream: OutputStream?) {
        var inStream: InputStream? = inputStream
        var outStream: OutputStream? = nil
        Stream.getBoundStreams(withBufferSize: bufferSize, inputStream: &inStream, outputStream: &outStream)
        return (inStream, outStream)
    }
}
04.11.2019
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..