bugfix> android > 投稿

Alexa Voice Serviceを統合したAndroidアプリを開発して、Reverbなどのアプリを開発する予定です。以下は私が試したものです...

  1. AVS Device SDKを確認し、Androidに実装するための適切なガイドを入手できます。

  2. チェック済みhttps://github.com/willblaschko/AlexaAndroid 、それを機能させることができませんでした。

  3. 自分自身を実装する予定で、以下は私がやったことです。

    a。統合化ログインフレームワークは、正常にログインしてトークンを取得できました。

    b。サウンドレコーダーを作成し、ローカルで録音および再生することができました。

    c。音声を送信するリクエストを作成しましたhttps://avs-alexa-eu.amazon.com/v20160207/events

[更新しました]

shortArrayをByte配列に変更した後、応答を得ていますが、MediaPlayerが応答mp3を再生できないという問題があります。準備時にエラーが発生します

package com.example.anoopmohanan.alexaandroid
import android.content.Context
import android.media.*
import android.media.AudioFormat.ENCODING_PCM_16BIT
import android.media.AudioFormat.CHANNEL_CONFIGURATION_MONO
import android.os.Environment
import android.os.Environment.getExternalStorageDirectory
import java.io.*
import com.example.anoopmohanan.alexaandroid.ResponseParser.getBoundary
import okhttp3.*
import org.jetbrains.anko.doAsync
import org.json.JSONObject
import java.nio.file.Files.exists
import okhttp3.OkHttpClient
import java.net.HttpURLConnection
import android.os.Looper
import android.os.PowerManager
import android.util.Log
import okio.BufferedSink
import okhttp3.RequestBody
import org.apache.commons.io.FileUtils
import org.jetbrains.anko.Android
import org.jetbrains.anko.runOnUiThread
import org.jetbrains.anko.toast
import java.util.*
import okhttp3.ResponseBody
import okio.Buffer
import com.example.anoopmohanan.alexaandroid.SoundRecorder.LoggingInterceptor
import android.media.MediaDataSource



class SoundRecorder(context: Context) {
    private var appcontext: Context? = null
    private var recording = false
    val MEDIA_JSON = MediaType.parse("application/json; charset=utf-8")
    val MEDIA_TYPE_AUDIO = MediaType.parse("application/octet-stream")
    var accessToken = ""
    private var mediaPlayer: MediaPlayer? = null
    private var streamToSend:ByteArray? = null
    private val client = OkHttpClient.Builder()
            .addInterceptor(LoggingInterceptor())
            .build()
    init {
        this.appcontext = context
    }
    fun startRecording(accessToken: String){
        this.accessToken = accessToken
        doAsync {
            startRecord()
        }
    }
    fun stopRecording(){
        doAsync {
            stopRecord()
        }
    }
    fun playRecording(){
        doAsync {
            playRecord()
        }
    }
    private fun stopRecord(){
        recording = false
        //val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
        //sendAuio(file)
    }
    fun playRecord() {
        val file = File(Environment.getExternalStorageDirectory(), "speech2.mp3")
            val mplayer = MediaPlayer()
            mplayer.setDataSource(file.path)
            mplayer.prepare()
            mplayer.start()
//        val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
//
//        val shortSizeInBytes = java.lang.Short.SIZE / java.lang.Byte.SIZE
//
//        val bufferSizeInBytes = (file.length() / shortSizeInBytes).toInt()
//        val audioData = ByteArray(bufferSizeInBytes)
//
//        try {
//            val inputStream = FileInputStream(file)
//            val bufferedInputStream = BufferedInputStream(inputStream)
//            val dataInputStream = DataInputStream(bufferedInputStream)
//
//            var i = 0
//            while (dataInputStream.available() > 0) {
//                audioData[i] = dataInputStream.readByte()
//                i++
//            }
//
//            dataInputStream.close()
//
//            val audioTrack = AudioTrack.Builder()
//                    .setAudioAttributes(AudioAttributes.Builder()
//                            .setUsage(AudioAttributes.USAGE_MEDIA)
//                            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
//                            .build())
//                    .setAudioFormat(AudioFormat.Builder()
//                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
//                            .setSampleRate(16000)
//                            .setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build())
//                    .setBufferSizeInBytes(bufferSizeInBytes)
//                    .setTransferMode(AudioTrack.MODE_STREAM)
//                    .build()
//
//            audioTrack.play()
//            audioTrack.write(audioData, 0, bufferSizeInBytes)
//
//
//        } catch (e: FileNotFoundException) {
//            e.printStackTrace()
//        } catch (e: IOException) {
//            e.printStackTrace()
//        }
    }

    private fun startRecord() {
        val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
        try {
            file.createNewFile()
            val outputStream = FileOutputStream(file)
            val bufferedOutputStream = BufferedOutputStream(outputStream)
            val dataOutputStream = DataOutputStream(bufferedOutputStream)
            val minBufferSize = AudioRecord.getMinBufferSize(16000,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT)

            val audioData = ByteArray(minBufferSize)
            val audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,
                    16000,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    800)
            if (audioRecord.recordingState != AudioRecord.RECORDSTATE_STOPPED){
                this.appcontext?.runOnUiThread {
                    toast("No recording source available")
                }
                return
            }
            recording = true
            audioRecord.startRecording()
            if (audioRecord.recordingState != AudioRecord.RECORDSTATE_RECORDING){
                this.appcontext?.runOnUiThread {
                    toast("Someone is still recording")
                }
                recording = false
                audioRecord.stop()
                audioRecord.release()
                return
            }
            this.appcontext?.runOnUiThread {
                toast("Recording started hurray")
            }
            while (recording) {
                audioRecord.read(audioData, 0, minBufferSize)
                dataOutputStream.write(audioData,0,minBufferSize)
//                for (i in 0 until numberOfShort) {
//                    dataOutputStream.writeShort(audioData[i].toInt())
//                }
            }
            audioRecord.stop()
            audioRecord.release()
            dataOutputStream.close()
            sendAuio(file)
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    fun sendAuio(audio: File){
        streamToSend = audio.readBytes()
        val requestBody = MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("metadata","metadata",RequestBody.create(MEDIA_JSON, generateSpeechMetadata()))
                .addFormDataPart("audio", "test.pcm",
                        RequestBody.create(MEDIA_TYPE_AUDIO, streamToSend))
                .build()
        val request = Request.Builder()
                .url("https://avs-alexa-eu.amazon.com/v20160207/events")
                .addHeader("Authorization","Bearer $accessToken")
                .post(requestBody)
                .build()
        print (request.body().toString())
        client.newCall(request).execute().use({ response ->
            if (!response.isSuccessful()) throw IOException("Unexpected code $response")
            val items = if (response.code() == HttpURLConnection.HTTP_NO_CONTENT)
                AvsResponse()
            else
                ResponseParser.parseResponse(response.body()!!.byteStream(), getBoundary(response))
            if (items.size > 0){
                handle(items)
            }
            System.out.println("[TRACE]"+response.body()!!.string())
        })
    }
    @Throws(IOException::class)
    fun toByteArray(`in`: InputStream): ByteArray {
        val out = ByteArrayOutputStream()
        var read = 0
        val buffer = ByteArray(1024)
        while (read != -1) {
            read = `in`.read(buffer)
            if (read != -1)
                out.write(buffer, 0, read)
        }
        out.close()
        return out.toByteArray()
    }
    private fun generateSpeechMetadata(): String {
        val messageId = UUID.randomUUID().toString();
        val dialogId = UUID.randomUUID().toString();
        return  "{\"event\": {\"header\": {\"namespace\": \"SpeechRecognizer\",\"name\": \"Recognize\",\"messageId\": \"$messageId\",\"dialogRequestId\": \"$dialogId\"},\"payload\": {\"profile\": \"CLOSE_TALK\", \"format\": \"AUDIO_L16_RATE_16000_CHANNELS_1\"}},\"context\": [{\"header\": {\"namespace\": \"AudioPlayer\",\"name\": \"PlaybackState\"},\"payload\": {\"token\": \"\",\"offsetInMilliseconds\": 0,\"playerActivity\": \"FINISHED\"}}, {\"header\": {\"namespace\": \"SpeechSynthesizer\",\"name\": \"SpeechState\"},\"payload\": {\"token\": \"\",\"offsetInMilliseconds\": 0,\"playerActivity\": \"FINISHED\"}}, { \"header\" : { \"namespace\" : \"Alerts\", \"name\" : \"AlertsState\" }, \"payload\" : { \"allAlerts\" : [ ], \"activeAlerts\" : [ ] } }, {\"header\": {\"namespace\": \"Speaker\",\"name\": \"VolumeState\"},\"payload\": {\"volume\": 25,\"muted\": false}}]}"
//        return "{\n" +
//                "\"messageHeader\": {\n" +
//                "\"deviceContext\": [\n" +
//                "{\n" +
//                "\"name\": \"playbackState\",\n" +
//                "\"namespace\": \"AudioPlayer\",\n" +
//                "\"payload\": {\n" +
//                "\"streamId\": \"\",\n" +
//                "\"offsetInMilliseconds\": \"\",\n" +
//                "\"playerActivity\": \"IDLE\"\n" +
//                "}\n" +
//                "}\n" +
//                "]\n" +
//                "},\n" +
//                "\"messageBody\": {\n" +
//                "\"profile\": \"doppler-scone\",\n" +
//                "\"locale\": \"en-us\",\n" +
//                "\"format\": \"audio/L16; rate=16000; channels=1\"\n" +
//                "}\n" +
//                "}"
    }

    private val audioRequestBody = object : RequestBody() {
        override fun contentType(): MediaType? {
            return MediaType.parse("application/octet-stream")
        }
        @Throws(IOException::class)
        override fun writeTo(sink: BufferedSink?) {
            //while our recorder is not null and it is still recording, keep writing to POST data
            sink!!.write(streamToSend)
        }
    }
    inner class LoggingInterceptor : Interceptor {
        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): Response {
            val request = chain.request()
            val t1 = System.nanoTime()
            Log.d("OkHttp", String.format("--> Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()))
            val requestBuffer = Buffer()
            request.body()?.writeTo(requestBuffer)
            Log.d("OkHttp", requestBuffer.readUtf8())
            val response = chain.proceed(request)
            val t2 = System.nanoTime()
            Log.d("OkHttp", String.format("<-- Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6, response.headers()))
            val contentType = response.body()?.contentType()
            val content = response.body()?.string()
            Log.d("OkHttp", content)
            val wrappedBody = ResponseBody.create(contentType, content)
            return response.newBuilder().body(wrappedBody).build()
        }
    }

    private fun getMediaPlayer(): MediaPlayer? {
        if (mediaPlayer == null) {
            mediaPlayer = MediaPlayer()
        }
        return mediaPlayer
    }
    fun handle(items:AvsResponse){
        for (item in items){
            handle(item)
        }
    }
    fun handle(item: AvsItem){

        if (item is AvsSpeakItem){
        }else{
            return
        }
        //cast our item for easy access
        //write out our raw audio data to a file
        val path = File(Environment.getExternalStorageDirectory(), "speech.mp3")
        //path.deleteOnExit()
        //val path = File(appcontext!!.getCacheDir(), System.currentTimeMillis().toString() + ".mp3")
        //var fos: FileOutputStream? = null
        try {
//            fos = FileOutputStream(path)
//            fos!!.write(item.audio)
//            fos.close()
            path.createNewFile()
            path.writeBytes(item.audio)
//            var ds = ByteArrayMediaDataSource(item.audio)
//            val fis = FileInputStream(path)
//            //play our newly-written file
//            val mplayer = MediaPlayer()
//            mplayer.setDataSource(path.path)
//            mplayer.prepare()
//            mplayer.start()
//            getMediaPlayer()?.setDataSource(fis.fd)
//            getMediaPlayer()?.prepare()
//            getMediaPlayer()?.start()
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: IllegalStateException) {
            e.printStackTrace()
        }
    }

    inner class ByteArrayMediaDataSource(private val data: ByteArray?) : MediaDataSource() {
        init {
            assert(data != null)
        }
        @Throws(IOException::class)
        override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
            System.arraycopy(data, position.toInt(), buffer, offset, size)
            return size
        }
        @Throws(IOException::class)
        override fun getSize(): Long {
            return data?.size?.toLong()!!
        }
        @Throws(IOException::class)
        override fun close() {
            // Nothing to do here
        }
    }
}

ALEXAからの回答

--------abcde123
Content-Type: application/json; charset=UTF-8
{"directive":{"header":{"namespace":"SpeechSynthesizer","name":"Speak","messageId":"d58c83fe-377f-4d1d-851b-a68cf5686280","dialogRequestId":"d83b8496-e6a5-4fc5-b07b-32f70acd1f15"},"payload":{"url":"cid:2aed5305-081d-4624-b2ba-ef51eba6aa32_1353236331","format":"AUDIO_MPEG","token":"amzn1.as-ct.v1.Domain:Application:Knowledge#ACRI#2aed5305-081d-4624-b2ba-ef51eba6aa32"}}}
--------abcde123
Content-ID: <2aed5305-081d-4624-b2ba-ef51eba6aa32_1353236331>
Content-Type: application/octet-stream
ID3#TSSELavf57.71.100dUC\[QwwD뻝q    \[D*An. .`>X>>@|L?&|>4=A?w'<<Ɨd"_h{[M̢8j8ǙbA
@=$T2bX2LM~NBPp˧Eh$`hoV
ԁh3}ɧ2/_74g挺`;*
W`dq՗HY)@mN<fG?l74(
    ˚z
BD)jy7uӹ\ۭc|>)qcsZQN} QppҜ{_]>iWw$Yd!.VEi]
<ƹYK.l6$̄)uLPGⰞRTFK>cr{\UK.|-EWGWȍ{3UN6]1tX@jr`5ka5/d V
WzLٺt?Aɬg%Я;la N%
A`r:ۙJSD3\´P y%eWOG5As
۪<*qfom
d !JjadR"=2vg?ZzrKZrV*Y2ش!XFOI EP99
Fdmy;ꡐ^JJ5С/\   _-LuafVMFZT-TUadRwO(A.2>sʳ!Y&)h`x<RR<AC|pTi"`k9#ɱX*%AP0CR7u+<VmFq ,ł)EhH굫.<hd#%[6h$kRO'IZ.VMX>!fqi+`:'j-Z6X *C
0S'9)y
回答 1 件
  • 私はそれを理解しました、私はOKHttpにLoggingInterceptorを使用していましたが、バイトを文字列に変換してログに記録し、何らかの理由でいくつかの文字を認識できず、それらの文字にいくつかのジャンク値を与えます理由として、これらの値はバイト配列にも保持されていました...これは、不正な形式のmp3ファイルを提供していました。インターセプターを削除した後...動作を開始しました...そのため、ステップ1が完了しました...さらに先に進みます。

あなたの答え