7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Android】MediaRecorderで録音機能を実装する

Last updated at Posted at 2025-08-28

はじめに

Androidで簡易的な録音アプリの作り方を解説します。
録音機能を実装するには、Androidの標準クラスであるMediaRecorderかAudioRecordを利用します。

MediaRecorderは録音からファイル保存までをまとめて処理できる高レベルAPIで、シンプルな録音アプリを素早く作りたいときに便利です。一方、AudioRecordはマイク入力の生データを直接扱える低レベルAPIで、波形解析や音声認識など高度な処理に適しています。

APIリファレンス:

今回は、より簡単に始められるMediaRecorderを使って録音アプリを実装していきます。

前提

検証環境は以下の通りです。

  • macOS Sequoia 15.6(Apple M4)
  • Android Studio Narwhal | 2025.1.1
  • Kotlin 2.0.21

実装していく

さっそく録音アプリを実装していきます。

権限の設定

まずは、録音を行うためにマイクの使用権限をアプリに追加します。
権限の追加は app/src/main/AndroidManifest.xml に次の1行を追記します。

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

この行は <manifest> タグの直下に追加します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- 録音用のマイク権限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application
        android:allowBackup="true"
        ...

これは「このアプリは録音機能を使う予定がある」とAndroidに知らせる宣言のようなものです。あとでAndroidの設定画面からアプリの権限設定でマイクの許可をオンにしますが、この宣言をしておかないとマイクの権限設定ができません。

画面周り

次に録音操作用の画面を作ります。今回は録音機能が主役なので、UIは録音の開始と停止を切り替えるだけの最小構成にします。下記のコードをMainActivityにそのままコピペして使えます。UIの詳細な説明は省きますが、興味があればJetpack Composeについて調べてみてください。

package com.example.myrecording // 適宜修正

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 起動時点で録音権限をチェック
        val granted = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.RECORD_AUDIO
        ) == PackageManager.PERMISSION_GRANTED

        setContent {
            var isRecording by remember { mutableStateOf(false) }

            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                Button(onClick = {
                    if (!isRecording) {
                        if (granted) {
                            // startRecording() は後で実装
                            isRecording = true
                        } else {
                            // 権限がない場合はトースト表示
                            Toast.makeText(
                                this@MainActivity,
                                "録音権限がありません",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    } else {
                        // stopRecording() は後で実装
                        isRecording = false
                    }
                }) {
                    Text(if (isRecording) "Stop" else "Start")
                }
            }
        }
    }
}

冒頭のこのコードは、アプリがマイクの録音権限(RECORD_AUDIO)をすでに持っているかどうかを判定するためのチェック処理です。

val granted = ContextCompat.checkSelfPermission(
    this,
    Manifest.permission.RECORD_AUDIO
) == PackageManager.PERMISSION_GRANTED

MediaRecorderを実装

まずMediaRecorderのインスタンスを作成したいのですが、従来のコンストラクタが非推奨になっており、APIレベル31から追加されたコンストラクタと使い分ける必要があります。そのため、次のようにインスタンスを生成する関数を作ります。

import android.media.MediaRecorder
import android.os.Build

// ...

private fun createMediaRecorder(): MediaRecorder {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        // API 31以上は新コンストラクタ
        MediaRecorder(this)
    } else {
        // API 30以下は旧コンストラクタ(非推奨だが互換性のため必要)
        @Suppress("DEPRECATION")
        MediaRecorder()
    }
}

録音開始処理

次にMediaRecorderインスタンスを使って録音処理を開始するstartRecording()メソッドを作ります。(冒頭setContent内のコメントアウト解除をお忘れなく)

import java.io.File

// ...

private var recorder: MediaRecorder? = null

private fun startRecording() {
    if (recorder != null) return

    val file = File(getExternalFilesDir(null)!!, "rec_${System.currentTimeMillis()}.m4a")

    recorder = createMediaRecorder().apply {
        setAudioSource(MediaRecorder.AudioSource.MIC)           // マイク入力
        setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)      // コンテナ形式
        setAudioEncoder(MediaRecorder.AudioEncoder.AAC)         // 音声コーデック
        setAudioEncodingBitRate(128_000)                        // ビットレート
        setAudioSamplingRate(44_100)                            // サンプリングレート
        setOutputFile(file.absolutePath)                        // 保存先
        prepare()                                                // 準備
        start()                                                  // 録音開始
    }
}

ここでは MediaRecorder に対して必要なパラメータを順番に設定しています。

  • setAudioSource(...)
    録音の入力元を指定する。マイク入力を使いたいのでMediaRecorder.AudioSource.MICを指定します。

  • setOutputFormat(...)
    録音データをどのファイル形式で保存するかを決める。ここでは MediaRecorder.OutputFormat.MPEG_4 を指定して MP4 コンテナ形式にしています。

  • setAudioEncoder(...)
    録音した音声をどの方式で圧縮するかを決める。

  • setAudioEncodingBitRate(...)
    ビットレート(音声データの情報量)を指定する。ここでは128000(=128kbps)とする。

  • setAudioSamplingRate(...)
    サンプリングレートを指定する。ここでは44100Hzに設定し、CD音質相当で録音できるようにしています。

  • setOutputFile(...)
    実際に書き込むファイルのパスを指定します。ここでは getExternalFilesDir(null)以下に .m4a ファイルとして保存します。

録音停止処理

録音を終えるときは、ファイルを書き終えてリソースを解放します。画面を閉じるときに録音が走っていない保証はないので、onDestroyでも保険としてリリースしておきます。

private fun stopRecording() {
    recorder?.let {
        try {
            it.stop()
        } catch (_: IllegalStateException) {
        }
        it.release()
    }
    recorder = null
}

override fun onDestroy() {
    super.onDestroy()
    recorder?.release()
    recorder = null
}

音声ファイル形式について

今回指定している拡張子.m4aとはなにか?
.m4aはMPEG-4 Audioの略で、もともと動画ファイルとしてよく使われるMP4を「音声だけに使ったときの拡張子」です。動画の部分を省いて、音声だけを保存するための形式だと思えばOKです。中身は主に「AAC」(Advanced Audio Coding) という方式で圧縮されていて、スマホやPCなど多くの環境でそのまま再生できます。

AndroidのMediaRecorderではMP3を直接保存することができません。
その代わりに、録音した音声は標準的にAACをm4aファイルとして保存する形になります。

MediaRecorderは録音した音声をリアルタイムでエンコード(圧縮変換)しながら保存 する仕組みであるため、OutputFormatで選べる形式は「圧縮済みのコンテナ形式」に限られているようです。(つまり、非圧縮のPCMやRAWは選べない)

録音してみる

手元に実機がなかったのでエミュレータで録音してみました。

エミュレータで録音するにはPCのマイクに入力した音を認識させる必要があるため、次のようにエミュレータのマイク設定をしてください。

  • エミュレータを起動したら「Running Devices」を開き三点アイコンを押して「Extended Controls」を開く(Shift2回押しして「Extended Controls」と入力して検索してもOK)
  • 左のメニューから「Microphone」を選択
  • 「Virtual microphone uses host audio input」をオンにする

image.png

設定が完了したらエミュレータを起動し、Androidの設定画面からアプリにマイクの権限を与えてからアプリを起動して録音してみてください。

録音したファイルを取り出す

録音した音声をPCで再生するには、エミュレータ(または実機)の内部に保存されたファイルをPCにコピーします。保存先は、今回のコードの場合 アプリ専用の外部ストレージ領域 です。

保存パスの例:

/sdcard/Android/data/<パッケージ名>/files/

(例:/sdcard/Android/data/com.example.myrecording/files/rec_XXXX.m4a

ターミナルから以下のコマンドを実行してデスクトップにコピーします。(ファイル名はadb shell lsで確認してください)

adb pull /sdcard/Android/data/com.example.myrecording/files/<ファイル名>.m4a ~/Desktop/

Android StudioのDevice Explorerを使えばGUI操作で取り出すことも可能です。(説明は割愛)

PCにコピーした.m4aファイルはPC標準のメディアプレーヤーでそのまま再生できます。

完成コード

最後に完成系のコードを貼っておきます。

package com.example.myrecording

import android.Manifest
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import java.io.File

class MainActivity : ComponentActivity() {
    private var recorder: MediaRecorder? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 起動時点で録音権限をチェック
        val granted = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.RECORD_AUDIO
        ) == PackageManager.PERMISSION_GRANTED

        setContent {
            var isRecording by remember { mutableStateOf(false) }

            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                Button(onClick = {
                    if (!isRecording) {
                        if (granted) {
                            startRecording()
                            isRecording = true
                        } else {
                            Toast.makeText(
                                this@MainActivity,
                                "録音権限がありません",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    } else {
                        stopRecording()
                        isRecording = false
                    }
                }) {
                    Text(if (isRecording) "Stop" else "Start")
                }
            }
        }
    }

    private fun createMediaRecorder(): MediaRecorder {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            // API 31以上は新コンストラクタ
            MediaRecorder(this)
        } else {
            // API 30以下は旧コンストラクタ(非推奨だが互換性のため必要)
            @Suppress("DEPRECATION")
            MediaRecorder()
        }
    }

    private fun startRecording() {
        if (recorder != null) return

        val file = File(getExternalFilesDir(null)!!, "rec_${System.currentTimeMillis()}.m4a")

        recorder = createMediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)           // マイク入力
            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)      // コンテナ形式
            setAudioEncoder(MediaRecorder.AudioEncoder.AAC)         // 音声コーデック
            setAudioEncodingBitRate(128_000)                        // ビットレート
            setAudioSamplingRate(44_100)                            // サンプリングレート
            setOutputFile(file.absolutePath)                        // 保存先
            prepare()                                                // 準備
            start()                                                  // 録音開始
        }
    }

    private fun stopRecording() {
        recorder?.let {
            try {
                it.stop()
            } catch (_: IllegalStateException) {
            }
            it.release()
        }
        recorder = null
    }

    override fun onDestroy() {
        super.onDestroy()
        recorder?.release()
        recorder = null
    }
}

今回の記事は以上です。ありがとうございました!

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?