Не удалось перекодировать из audio/l16;rate=48000;channel=1 в один из: audio/x-float-array; ставка=16000; каналы=1,

В настоящее время я работаю над роботом Pepper от Softbanks и пытаюсь использовать решение Watson для преобразования речи в текст для удаленной потоковой передачи аудиобуферов Pepper с использованием протокола websocket.

Я использовал ответ на этот прежний вопрос проблемы удаленного звука робота NAO, чтобы найти способ получить доступ к аудиобуферам удаленного перца и этому проекту https://github.com/ibm-dev/watson-streaming-stt, чтобы узнать, как использовать протокол websocket для использования Watson Streaming stt.

Однако после того, как я открываю свое веб-приложение, я начинаю отправлять буферы в Watson, и после нескольких отправок я получаю сообщение об ошибке: «Невозможно перекодировать из audio/l16;rate=48000;channel=1 в один из: audio/x-float -множество; ставка=16000; каналы=1'

Каждый раз, когда я пытаюсь отправить звуковой буфер Pepper в Watson, он не может его понять.

Я сравнил данные, которые я отправляю, с данными, отправленными в примере Watson streaming stt (используя потоковую передачу pyaudio с микрофона вместо потоковой передачи буфера Pepper), и я не вижу никакой разницы. Оба раза я почти уверен, что отправляю строку, содержащую необработанные фрагменты байтов. Это то, что Watson запрашивает в своей документации.

Я пытаюсь отправить фрагменты по 8192 байта с частотой дискретизации 48 кГц, и я могу легко преобразовать аудиобуфер Pepper в шестнадцатеричный формат, поэтому я не понимаю, почему Watson не может его перекодировать.

Вот мой код:

# -*- coding: utf-8 -*-

#!/usr/bin/env python

import argparse
import base64
import configparser
import json
import threading
import time
from optparse import OptionParser
import naoqi
import numpy as np
import sys
from threading import Thread
import ssl

import websocket
from websocket._abnf import ABNF

CHANNELS = 1

NAO_IP = "172.20.10.12" 

class SoundReceiverModule(naoqi.ALModule):
    """
    Use this object to get call back from the ALMemory of the naoqi world.
    Your callback needs to be a method with two parameter (variable name, value).
    """

    def __init__( self, strModuleName, strNaoIp):
        try:
            naoqi.ALModule.__init__(self, strModuleName );
            self.BIND_PYTHON( self.getName(),"callback" );
            self.strNaoIp = strNaoIp;
            self.outfile = None;
            self.aOutfile = [None]*(4-1); # ASSUME max nbr channels = 4
            self.FINALS = []
            self.RECORD_SECONDS = 20
            self.ws_open = False
            self.ws_listening = ""

            # init data for websocket interfaces
            self.headers = {}
            self.userpass = "" #userpass and password
            self.headers["Authorization"] = "Basic " + base64.b64encode(
                self.userpass.encode()).decode()
            self.url = ("wss://stream.watsonplatform.net//speech-to-text/api/v1/recognize"
                   "?model=fr-FR_BroadbandModel")

        except BaseException, err:
            print( "ERR: abcdk.naoqitools.SoundReceiverModule: loading error: %s" % str(err) );

    # __init__ - end
    def __del__( self ):
        print( "INF: abcdk.SoundReceiverModule.__del__: cleaning everything" );
        self.stop();

    def start( self ):
        audio = naoqi.ALProxy( "ALAudioDevice", self.strNaoIp, 9559 );
        self.nNbrChannelFlag = 3; # ALL_Channels: 0,  AL::LEFTCHANNEL: 1, AL::RIGHTCHANNEL: 2; AL::FRONTCHANNEL: 3  or AL::REARCHANNEL: 4.
        self.nDeinterleave = 0;
        self.nSampleRate = 48000;
        audio.setClientPreferences( self.getName(),  self.nSampleRate, self.nNbrChannelFlag, self.nDeinterleave ); # setting same as default generate a bug !?!
        audio.subscribe( self.getName() );

        #openning websocket app
        self._ws = websocket.WebSocketApp(self.url,
                                header=self.headers,
                                on_open = self.on_open,
                                on_message=self.on_message,
                                on_error=self.on_error,
                                on_close=self.on_close) 


        sslopt={"cert_reqs": ssl.CERT_NONE}
        threading.Thread(target=self._ws.run_forever, kwargs = {'sslopt':sslopt}).start()

        print( "INF: SoundReceiver: started!" );

    def stop( self ):
        print( "INF: SoundReceiver: stopping..." );
        audio = naoqi.ALProxy( "ALAudioDevice", self.strNaoIp, 9559 );
        audio.unsubscribe( self.getName() );  
        print( "INF: SoundReceiver: stopped!" );

        print "INF: WebSocket: closing..."
        data = {"action": "stop"}
        self._ws.send(json.dumps(data).encode('utf8'))
        # ... which we need to wait for before we shutdown the websocket
        time.sleep(1)
        self._ws.close()
        print "INF: WebSocket: closed"

        if( self.outfile != None ):
            self.outfile.close();

    def processRemote( self, nbOfChannels, nbrOfSamplesByChannel, aTimeStamp, buffer ):
        """
        This is THE method that receives all the sound buffers from the "ALAudioDevice" module"""

        print "receiving buffer"

        # self.data_to_send = self.data_to_send + buffer

        # print len(self.data_to_send)
        #self.data_to_send = ''.join( [ "%02X " % ord( x ) for x in buffer ] ).strip()
        self.data_to_send = buffer

        #print("buffer type :", type(data))
        #print("buffer :", buffer)
        #~ print( "process!" ); 
        print( "processRemote: %s, %s, %s, lendata: %s, data0: %s (0x%x), data1: %s (0x%x)" % (nbOfChannels, nbrOfSamplesByChannel, aTimeStamp, len(buffer), buffer[0],ord(buffer[0]),buffer[1],ord(buffer[1])) );

        if self.ws_open == True and self.ws_listening == True:
            print "sending data"
            self._ws.send(self.data_to_send, ABNF.OPCODE_BINARY)
            print "data sent"
            #print self.data_to_send

        aSoundDataInterlaced = np.fromstring( str(buffer), dtype=np.int16 );
#        
        aSoundData = np.reshape( aSoundDataInterlaced, (nbOfChannels, nbrOfSamplesByChannel), 'F' );


        # print "processRemote over"
    # processRemote - end

    def on_message(self, ws, msg):

        print("message")
        data = json.loads(msg)
        print data
        if "state" in data:
            if data["state"] == "listening":
                self.ws_listening = True


        if "results" in data:
            if data["results"][0]["final"]:
                self.FINALS.append(data)
            # This prints out the current fragment that we are working on
            print(data['results'][0]['alternatives'][0]['transcript'])


    def on_error(self, ws, error):
        """Print any errors."""
        print(error)


    def on_close(self, ws):
        """Upon close, print the complete and final transcript."""
        transcript = "".join([x['results'][0]['alternatives'][0]['transcript']
                              for x in self.FINALS])
        print("transcript :", transcript)
        self.ws_open = False


    def on_open(self, ws): 
        """Triggered as soon a we have an active connection."""
#        args = self._ws.args

        print "INF: WebSocket: opening"
        data = {
            "action": "start",
            # this means we get to send it straight raw sampling
            "content-type": "audio/l16;rate=%d;channel=1" % self.nSampleRate,
            "continuous": True,
            "interim_results": True,
            # "inactivity_timeout": 5, # in order to use this effectively
            # you need other tests to handle what happens if the socket is
            # closed by the server.
            "word_confidence": True,
            "timestamps": True,
            "max_alternatives": 3
        }

        # Send the initial control message which sets expectations for the
        # binary stream that follows:
        self._ws.send(json.dumps(data).encode('utf8'))
        # Spin off a dedicated thread where we are going to read and
        # stream out audio.

        print "INF: WebSocket: opened"
        self.ws_open = True


    def version( self ):
        return "0.6";

def main():

    """initialisation 
    """
    parser = OptionParser()
    parser.add_option("--pip",
        help="Parent broker port. The IP address or your robot",
        dest="pip")
    parser.add_option("--pport",
        help="Parent broker port. The port NAOqi is listening to",
        dest="pport",
        type="int")
    parser.set_defaults(
        pip=NAO_IP,
        pport=9559)

    (opts, args_) = parser.parse_args()
    pip   = opts.pip
    pport = opts.pport

    # We need this broker to be able to construct
    # NAOqi modules and subscribe to other modules
    # The broker must stay alive until the program exists
    myBroker = naoqi.ALBroker("myBroker",
       "0.0.0.0",   # listen to anyone
       0,           # find a free port and use it
       pip,         # parent broker IP
       pport)       # parent broker port  


    """fin initialisation 
    """

    global SoundReceiver
    SoundReceiver = SoundReceiverModule("SoundReceiver", pip) #thread1
    SoundReceiver.start()

    try:
        while True:
            time.sleep(1)
            print "hello"
    except KeyboardInterrupt:
        print "Interrupted by user, shutting down"
        myBroker.shutdown()
        SoundReceiver.stop()
        sys.exit(0)

if __name__ == "__main__":
    main()

Я был бы признателен, если бы у кого-нибудь была идея, как обойти эту ошибку или что попытаться получить полезную информацию. Сначала я полагал, что отправляю Watson «неправильные» данные, однако после множества попыток я понятия не имею, как решить эту проблему.

Спасибо большое,

Алекс


person Alexandre Chenu    schedule 13.07.2018    source источник
comment
Разве основная проблема не в том, что вы отправляете 48 кГц, в то время как Watson API требует 16 кГц?   -  person JLS    schedule 13.07.2018
comment
@JLS Я не думаю, что ошибка связана с частотой дискретизации, когда я пытаюсь сделать то же самое, используя pyAudio на своем компьютере (github.com/ibm-dev/watson-streaming-stt), я также установил частоту дискретизации на 48 кГц, и все работает отлично. Принимая во внимание, что это все еще не работает, когда я устанавливаю частоту дискретизации Pepper на 16 кГц, ха-ха   -  person Alexandre Chenu    schedule 13.07.2018
comment
Читая ошибку, похоже, что вы отправляете 16-битные данные выборки и ожидаете с плавающей запятой (например, с плавающей запятой 32 бита)...   -  person Alexandre Mazel    schedule 16.07.2018
comment
@AlexandreChenu Я знаю, что это не отвечает на ваш вопрос, но вы пытались использовать Python SDK? Существует пример, который вы можете использовать github.com /watson-developer-cloud/python-sdk/blob/master/. Убедитесь, что вы подключили правильный буфер.   -  person German Attanasio    schedule 28.12.2019