Android audioRecord to wav without saving as File (Kotlin)

641 Views Asked by At

I'm using AudioRecord to record an audio. What I need is to save the recorded audio as one Array/Variable in a playable format (wav). I need to use AudioRecord because parallel to getting the audio I run a speech recognizer. So MediaRecorder is no option. I actually got a working solution. The only problem is the audio is written to a file. Also my function for adding the wav header to the pcm needs a file and stores it again in a file.

What I am looking for: do all this without saving the audio to a file. In the end I would like to have the audio in an array or something similar to send this audio to Flutter and use it there.

My working solution (with saving audio to file):

private fun record() {
            val sampleRate = 16000
            val frameLength = 512
            val bufferSize = (sampleRate / 2).coerceAtLeast(minBufferSize)
            var audioRecord: AudioRecord? = null
            var buffer = ShortArray(frameLength)
            var bufferByte: ByteArray

            var fileName =  Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + "/audio" + ".pcm"
       
            audioRecord = AudioRecord(
                    MediaRecorder.AudioSource.MIC,
                    sampleRate,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    bufferSize)
            audioRecord.startRecording()

            var outputStream: FileOutputStream?
            try {
                outputStream = FileOutputStream(fileName)
            } catch (e: FileNotFoundException) {
                return
            }
            while (!stop.get()) {
                var readBufferShortLength = audioRecord.read(buffer, 0, buffer.size)

                bufferByte = getBytes(buffer)
                try {
                    outputStream!!.write(bufferByte, 0, bufferByte.size)
                    // clean up file writing operations
                } catch (e: IOException) {
                    e.printStackTrace()
                }
             }
             outputStream.flush()
             outputStream.close()
          }

For the Voice Recognition it needs a ShortArray. For adding the wav header it needs ByteArray. For converting the ShortArray to ByteArray:

fun shortToByte(shortArray: ShortArray): ByteArray {
        val buffer = ByteBuffer.allocate(shortArray.size * 2)
        buffer.order(ByteOrder.LITTLE_ENDIAN)
        buffer.asShortBuffer().put(shortArray)
        val bytes = buffer.array()
        return bytes
    }

Converting adding wav header:

@Throws(IOException::class)
    private fun rawToWave(rawFile: File, waveFile: File, sampleRate: Int) {
        val rawData = ByteArray(rawFile.length().toInt())
        var input: DataInputStream? = null
        try {
            input = DataInputStream(FileInputStream(rawFile))
            input.read(rawData)
        } finally {
            input?.close()
        }
        var output: DataOutputStream? = null
        try {
            output = DataOutputStream(FileOutputStream(waveFile))
            // WAVE header
            // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
            writeString(output, "RIFF") // chunk id
            writeInt(output, 36 + rawData.size) // chunk size
            writeString(output, "WAVE") // format
            writeString(output, "fmt ") // subchunk 1 id
            writeInt(output, 16) // subchunk 1 size
            writeShort(output, 1.toShort()) // audio format (1 = PCM)
            writeShort(output, 1.toShort()) // number of channels
            writeInt(output, sampleRate) // sample rate
            writeInt(output, sampleRate * 2) // byte rate     --------- Here changen maybe
            writeShort(output, 2.toShort()) // block align
            writeShort(output, 16.toShort()) // bits per sample
            writeString(output, "data") // subchunk 2 id
            writeInt(output, rawData.size) // subchunk 2 size
            // Audio data (conversion big endian -> little endian)
            val shorts = ShortArray(rawData.size / 2)
            ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts)
            val bytes: ByteBuffer = ByteBuffer.allocate(shorts.size * 2)
            for (s in shorts) {
                bytes.putShort(s)
            }
            output.write(fullyReadFileToBytes(rawFile))
        } finally {
            output?.close()
        }
    }

    @Throws(IOException::class)
    fun fullyReadFileToBytes(f: File): ByteArray? {
        val size = f.length().toInt()
        val bytes = ByteArray(size)
        val tmpBuff = ByteArray(size)
        val fis = FileInputStream(f)
        try {
            var read = fis.read(bytes, 0, size)
            if (read < size) {
                var remain = size - read
                while (remain > 0) {
                    read = fis.read(tmpBuff, 0, remain)
                    System.arraycopy(tmpBuff, 0, bytes, size - remain, read)
                    remain -= read
                }
            }
        } catch (e: IOException) {
            throw e
        } finally {
            fis.close()
        }
        return bytes
    }

    @Throws(IOException::class)
    private fun writeInt(output: DataOutputStream, value: Int) {
        output.write(value shr 0)
        output.write(value shr 8)
        output.write(value shr 16)
        output.write(value shr 24)
    }

    @Throws(IOException::class)
    private fun writeShort(output: DataOutputStream, value: Short) {
        var v = value.toInt()
        output.write(v shr 0)
        output.write(v shr 8)
    }

    @Throws(IOException::class)
    private fun writeString(output: DataOutputStream, value: String) {
        for (i in 0 until value.length) {
            output.write(value[i].code)     //  ------ Here changen maybe
        }
    }

I really appreciate your support.

0

There are 0 best solutions below