はじめに
ピッチ(音高)を測るチューナーアプリを作るためライブラリを探していたら TarsosDSP というものを見つけた。 Android でも使用できるみたいだがかなり古い。TarsosDSP を使用した Android プロジェクトの GitHub リポジトリを見つけたがこれらも古い。
記事を書いている 2024.02 時点の最新の環境でも TarsosDSP を使えるのか検証する。
環境
- Android Studio
Android Studio Hedgehog | 2023.1.1 Patch 1 - Target API Level 34
- Empty Activity テンプレート (= Jetpack Compose)
1. Android プロジェクトを作成する
Empty Activity テンプレートでAndroid プロジェクトを新規作成する。
2. TarsosDSP のセットアップ
TarsosDSP の GitHub リポジトリの README に記載されたリンク 「TarsosDSP release directory」をクリックする。
TarsosDSP-Android-latest.jar をダウンロードする。
app/libs/TarsosDSP-Android-latest.jar として配置する。
app モジュールから TarsosDSP-Android-latest.jar を参照できるようにする。
dependencies {
+ implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
}
3. AndroidManifest.xml にパーミッションを追加する
マイクのパーミッション RECORD_AUDIO
を追加する。
<?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" />
...
4. TarsosDSP API を使ってコーディングする
Empty Activity テンプレートで作られた MainActivity を利用して TarsosDSP API を使ったコーディングをする。
マイクから入力した音のピッチが測れるよう AudioDispatcherFactory.fromDefaultMicrophone
! を使用する。
class MainActivity : ComponentActivity() {
private val pitchInHzStateFlow = MutableStateFlow(0.0f)
private val noteStateFlow = MutableStateFlow("")
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) {
initTarsosDsp()
} else {
//TODO: エラーダイアログを表示する
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestPermissionLauncher.launch(android.Manifest.permission.RECORD_AUDIO)
setContent {
val pitchInHzState by pitchInHzStateFlow.collectAsState()
val noteState by noteStateFlow.collectAsState()
HelloTunerTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting(hz = pitchInHzState, note = noteState)
}
}
}
}
private fun initTarsosDsp() {
val dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050, 1024, 0)
val pdh = PitchDetectionHandler { res, e ->
val pitchInHz = res.pitch
runOnUiThread { processPitch(pitchInHz) }
}
val pitchProcessor: AudioProcessor =
PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050f, 1024, pdh)
dispatcher.addAudioProcessor(pitchProcessor)
val audioThread = Thread(dispatcher, "Audio Thread")
audioThread.start()
}
private fun processPitch(pitchInHz: Float) {
pitchInHzStateFlow.value = pitchInHz
if(pitchInHz >= 110 && pitchInHz < 123.47) {
//A
noteStateFlow.value = "A"
}
else if(pitchInHz >= 123.47 && pitchInHz < 130.81) {
//B
noteStateFlow.value = "B"
}
else if(pitchInHz >= 130.81 && pitchInHz < 146.83) {
//C
noteStateFlow.value = "C"
}
else if(pitchInHz >= 146.83 && pitchInHz < 164.81) {
//D
noteStateFlow.value = "D"
}
else if(pitchInHz >= 164.81 && pitchInHz < 174.61) {
//E
noteStateFlow.value = "E"
}
else if(pitchInHz >= 174.61 && pitchInHz < 185) {
//F
noteStateFlow.value = "F"
}
else if(pitchInHz >= 185 && pitchInHz < 196) {
//G
noteStateFlow.value = "G"
}
}
}
@Composable
fun Greeting(
modifier: Modifier = Modifier,
hz: Float,
note: String
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Row {
Text(text = "ノート:")
Text(text = "${note}")
}
Row {
Text(text = "周波数:")
Text(text = "${hz.toString()}")
}
}
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
HelloTunerTheme {
Greeting(hz = 130.81f, note = "A")
}
}
完成
補足
TarsosDSP の GitHub リポジトリの README のとおり、以下のように dependencies
にパッケージを追加したり、
repositories {
maven {
name = "TarsosDSP repository"
url = "https://mvn.0110.be/releases"
}
}
dependencies {
implementation 'be.tarsos.dsp:core:2.5'
implementation 'be.tarsos.dsp:jvm:2.5'
}
ソースコードをビルドしてできた jar
を使うと、
git clone --depth 1 https://JorenSix@github.com/JorenSix/TarsosDSP.git
cd TarsosDSP
./gradlew build
javax.sound.sampled.AudioRecord
を参照する箇所でビルドエラーとなるはず。理由は、javax.sound.sampled
パッケージは Android SDK に含まれてないからだ。 Android 用にビルドされた TarsosDSP-Android-latest.jar は javax.sound.sampled.AudioRecord
の代替として android.media.AudioRecord
を使用しているのでどうエラーは発生しない。
参考