HAMADAの語り草

興味のある技術のアウトプットをしたいと思います

鬼の2時間ハッカソン?でAndroidアプリの録音再生機能を実装した話

はじめに

会津大学学部3年のHAMADAです。この記事は「Aizu Advent Calendar 2023」18日目の記事です。今回は、鬼の2時間ハッカソンで試したことについて書いていきたいと思います。

経緯

つい最近、私が所属しているA-PxLで ”2時間ハッカソン” と題して、普段の定例会の2時間を使って、テーマが”クリスマス”のプロダクトを一本作り切るという鬼企画が開催されました。

A-PxLはXRのサークルなので、私もAndroidのARアプリを作成しようとしたのですが、ARCore周りの設定で1時間15分も溶かしてしまいました。流石にちょっと厳しそうだったので、方向転換をし、音声録音アプリケーションを作成することにしました。

一応テーマがクリスマスなので、プレゼントを届けに来たサンタさんに”メリークリスマス”と一言伝えられたら素敵やな、とか後付け的に思いついたので、テーマには沿ってると思います…

使用したもの・技術

実装

まず、録音をするためにAndroidManifest.xmlに以下を追記する。

<uses-permission android:name="android.permission.RECORD_AUDIO" />

MediaRecorderとMediaPlayerを使用して、

以下のように実装しました。

import android.media.MediaRecorder
import android.media.MediaPlayer

class AudioRecorder(private val filePath: String) {

    private var mediaRecorder: MediaRecorder? = null
    private var mediaPlayer: MediaPlayer? = null
    private var isRecording = false

    fun startRecording() {
        if (isRecording) return

        mediaRecorder = MediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            setOutputFile(filePath)
            prepare()
            start()
        }
        isRecording = true
    }

    fun stopRecording() {
        if (!isRecording) return

        mediaRecorder?.apply {
            stop()
            release()
        }
        mediaRecorder = null
        isRecording = false
    }

    fun playRecording() {
        mediaPlayer = MediaPlayer().apply {
            setDataSource(filePath)
            prepare()
            start()
        }
    }
}

MediaRecorder

setAudioSource(MediaRecorder.AudioSource.MIC) は音をマイクから取ってくるように設定しています。他にも電話から音をとったり、リンクから音を取ったりと色々設定できます。

setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) はアウトプットのフォーマットを設定しています。

setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)エンコードの形式を設定しています。

setOutputFile(filePath) は設定したファイルパスにファイルをアウトプットします。

prepare() で初期化を完了します。

start() でレコーダーを開始。

stop() でレコーダーを停止。

release() でリソースを解放。

MediaPlayer

setDataSource(filePath) は再生するソースをセットします。

prepare() で初期化を完了します。

start() で再生を開始。

上記のような処理を呼び出す際に、使っているハードでパーミッションがあるかどうかを確認するために、以下のような実装をしました。

    private fun handleRecording() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), 200)
        } else {
            audioRecorder?.startRecording()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 200 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            audioRecorder?.startRecording()
        }
    }

アプリがRECORD_AUDIOのパーミッションを持っているかどうか確認して、持っていれば、そのままレコーディングを開始し、持っていなければ以下のようなリクエストを表示します。

リクエストに対してのユーザの応答を確認し、許可が得られていれば録音を開始します。

Andoidの実機が手元になく、デモが撮れませんでした。ごめんなさい...

おまけ

UIとしては、本当にシンプルで雑なUIなのですが、ChatGPT君にいい感じの画像を作ってもらい背景にしたところそれっぽい見た目になりました。

Before

After

感想と展望

45分で雑な実装にはなってしまいましたが、パーミッション周りは触ったことがなかったので、良い学びになりました。二時間でのハッカソンはかなり鬼畜の所業と言わざるを得ませんが、実装したい機能、やったことない機能を勉強するにはちょうど良いので、ぜひ次もやってみたいと思います。ここまで読んでいただきありがとうございます。良いクリスマス、良い年末をお迎えください。

所属サークル

https://twitter.com/aizu_PxL

私のTwitter

https://twitter.com/AHMOS_HMD

私のgithub

ahmos0 (HAMADA) · GitHub

参考リンク

developer.android.com

developer.android.com