适用于 Android 的音频分类指南

借助 MediaPipe 音频分类器任务,您可以对音频数据进行分类。您可以使用此任务从一组经过训练的类别中识别声音事件。以下说明介绍了如何在 Android 应用中使用音频分类器。

如需详细了解此任务的功能、模型和配置选项,请参阅概览

代码示例

MediaPipe Tasks 示例代码是 Android 的音频分类器应用的简单实现。该示例使用实体 Android 设备上的麦克风持续对声音进行分类,还可以对存储在设备上的声音文件运行分类器。

您可以使用该应用作为基础来开发自己的 Android 应用,也可以在修改现有应用时参考该应用。音频分类器示例代码托管在 GitHub 上。

下载代码

以下说明介绍了如何使用 git 命令行工具创建示例代码的本地副本。

如需下载示例代码,请执行以下操作:

  1. 使用以下命令克隆 Git 代码库:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. (可选)将您的 Git 实例配置为使用稀疏检出,以便只有音频分类器示例应用的文件:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/audio_classifier/android
    

创建示例代码的本地版本后,您可以将项目导入 Android Studio 并运行应用。有关说明,请参阅 Android 设置指南

关键组件

以下文件包含此音频分类示例应用的关键代码:

初始设置

本部分介绍了专门针对使用音频分类器而设置开发环境和代码项目的关键步骤。如需了解如何为使用 MediaPipe 任务设置开发环境的一般信息(包括平台版本要求),请参阅 Android 设置指南

依赖项

音频分类器使用 com.google.mediapipe:tasks-audio 库。请将此依赖项添加到 Android 应用开发项目的 build.gradle 文件中。使用以下代码导入所需的依赖项:

dependencies {
    ...
    implementation 'com.google.mediapipe:tasks-audio:latest.release'
}

模型

MediaPipe 音频分类器任务需要使用与此任务兼容的经过训练的模型。如需详细了解音频分类器可用的经过训练的模型,请参阅任务概览“模型”部分

选择并下载模型,然后将其存储在项目目录中:

<dev-project-root>/src/main/assets

使用 BaseOptions.Builder.setModelAssetPath() 方法指定模型使用的路径。下一部分的代码示例将引用此方法。

在音频分类器示例代码中,模型在 AudioClassifierHelper.kt 文件中定义。

创建任务

您可以使用 createFromOptions 函数创建任务。createFromOptions 函数接受配置选项,包括运行模式、显示名称语言区域、结果数上限、置信度阈值以及类别许可名单或拒绝名单。如需详细了解配置选项,请参阅配置概览

音频分类器任务支持以下输入数据类型:音频片段和音频流。您需要在创建任务时指定与输入数据类型对应的运行模式。选择与输入数据类型对应的标签页,了解如何创建任务并运行推断。

音频片段

AudioClassifierOptions options =
    AudioClassifierOptions.builder()
        .setBaseOptions(
            BaseOptions.builder().setModelAssetPath("model.tflite").build())
        .setRunningMode(RunningMode.AUDIO_CLIPS)
        .setMaxResults(5)
        .build();
audioClassifier = AudioClassifier.createFromOptions(context, options);
    

音频流

AudioClassifierOptions options =
    AudioClassifierOptions.builder()
        .setBaseOptions(
            BaseOptions.builder().setModelAssetPath("model.tflite").build())
        .setRunningMode(RunningMode.AUDIO_STREAM)
        .setMaxResults(5)
        .setResultListener(audioClassifierResult -> {
             // Process the classification result here.
        })
        .build();
audioClassifier = AudioClassifier.createFromOptions(context, options);
    

借助音频分类器示例代码实现,用户可以在处理模式之间切换。这种方法会使任务创建代码更加复杂,可能不适合您的用例。您可以在 AudioClassifierHelperinitClassifier() 函数中看到模式切换代码。

配置选项

此任务具有以下 Android 应用的配置选项:

选项名称 说明 值范围 默认值
runningMode 设置任务的运行模式。音频分类器有两种模式:

AUDIO_CLIPS:针对独立音频片段运行音频任务的模式。

AUDIO_STREAM:在音频流(例如来自麦克风)上运行音频任务的模式。在此模式下,必须调用 resultListener,以设置监听器以异步接收分类结果。
{AUDIO_CLIPS, AUDIO_STREAM} AUDIO_CLIPS
displayNamesLocale 设置任务模型元数据中提供的显示名(如果有)要使用的标签语言。英语的默认值为 en。您可以使用 TensorFlow Lite Metadata Writer API 向自定义模型的元数据添加本地化标签。语言区域代码 en
maxResults 设置要返回的得分最高的分类结果的数量上限(可选)。如果小于 0,将返回所有可用的结果。 任何正数 -1
scoreThreshold 设置预测分数阈值,以替换模型元数据中提供的阈值(如果有)。低于此值的结果会被拒绝。 [0.0, 1.0] 未设置
categoryAllowlist 设置允许的类别名称的可选列表。如果为非空,则类别名称不在此集合中的分类结果将被滤除。系统会忽略重复或未知的类别名称。 此选项与 categoryDenylist 互斥,如果同时使用这两者,就会引发错误。 任何字符串 未设置
categoryDenylist 设置不允许使用的类别名称的可选列表。如果非空,则类别名称在此集合中的分类结果将被滤除。系统会忽略重复或未知的类别名称。此选项与 categoryAllowlist 互斥,同时使用这两者会导致错误。 任何字符串 未设置
resultListener 将结果监听器设置为在音频分类器处于音频流模式时异步接收分类结果。只能在跑步模式设为“AUDIO_STREAM”时使用 N/A 未设置
errorListener 设置可选的错误监听器。 N/A 未设置

准备数据

音频分类器支持音频片段和音频流。该任务会处理数据输入预处理,包括重新采样、缓冲和取景。但是,您必须先将输入音频数据转换为 com.google.mediapipe.tasks.components.containers.AudioData 对象,然后才能将其传递给音频分类器任务。

音频片段

import com.google.mediapipe.tasks.components.containers.AudioData;

// Load an audio on the user’s device as a float array.

// Convert a float array to a MediaPipe’s AudioData object.
AudioData audioData =
    AudioData.create(
        AudioData.AudioDataFormat.builder()
            .setNumOfChannels(numOfChannels)
            .setSampleRate(sampleRate)
            .build(),
        floatData.length);
audioData.load(floatData);
    

音频流

import android.media.AudioRecord;
import com.google.mediapipe.tasks.components.containers.AudioData;

AudioRecord audioRecord =
    audioClassifier.createAudioRecord(/* numChannels= */ 1, /* sampleRate= */ 16000);
audioRecord.startRecording();

...

// To get a one second clip from the AudioRecord object:
AudioData audioData =
    AudioData.create(
        16000 /*sample counts per second*/);
        AudioData.AudioDataFormat.create(audioRecord.getFormat()),
audioData.load(audioRecord)
    

运行任务

您可以调用与跑步模式对应的 classify 函数来触发推断。Audio Classifier API 会返回输入音频数据中识别出的音频事件的可能类别。

音频片段

AudioClassifierResult classifierResult = audioClassifier.classify(audioData);
    

音频流

// Run inference on the audio block. The classifications results will be available
// via the `resultListener` provided in the `AudioClassifierOptions` when
// the audio classifier was created.
audioClassifier.classifyAsync(audioBlock, timestampMs);
    

请注意以下几点:

  • 在音频流模式下运行时,您还必须为音频分类器任务提供时间戳,以跟踪流中的哪些音频数据用于推理。
  • 在音频片段模型中运行时,音频分类器任务会阻塞当前线程,直到处理完输入音频。为避免阻塞界面响应,请在后台线程中执行处理。

您可以查看使用音频片段运行音频分类器的示例,请参阅代码示例中的 AudioClassifierHelper 类。

处理和显示结果

运行推理后,音频分类器任务会返回输入音频中音频事件可能的类别列表。以下清单显示了此任务的输出数据示例:

AudioClassifierResult:
  Timestamp in microseconds: 100
  ClassificationResult #0:
    Timestamp in microseconds: 100  
    Classifications #0 (single classification head):
      head index: 0
      category #0:
        category name: "Speech"
        score: 0.6
        index: 0
      category #1:
        category name: "Music"
        score: 0.2
        index: 1

在 Android 应用中,任务会返回一个 ClassificationResult,其中包含 AudioClassifierResult 对象列表,表示音频事件的预测,包括类别标签和置信度分数。

音频片段

// In the audio clips mode, the classification results are for the entire audio
// clip. The results are timestamped AudioClassifierResult objects, each
// classifying an interval of the entire audio clip that starts at
// ClassificationResult.timestampMs().get().

for (ClassificationResult result : audioClassifierResult.classificationResults()) {
  // Audio interval start timestamp:
  result.timestampMs().get();
  // Classification result of the audio interval.
  result.classifications();
}
    

音频流

// In the audio stream mode, the classification results list only contains one
// element, representing the classification result of the audio block that
// starts at ClassificationResult.timestampMs in the audio stream.

ClassificationResult result = audioClassifierResult.classificationResults().get(0);
// The audio block start timestamp
audioClassifierResult.timestampMs();
// Alternatively, the same timestamp can be retrieved from
// result.timestampMs().get();

// Classification result.
result.classifications();
    

您可以在代码示例ProbabilitiesAdapter 类中查看示例,了解如何显示此任务返回的分类结果。