Android 音频

Android 操作系统为多媒体提供广泛的支持,包括音频和视频。 本指南重点介绍 Android 中的音频,并介绍如何使用内置音频播放器和录制器类以及低级音频 API 来播放和录制音频。 其中还介绍了如何处理其他应用程序广播的音频事件,以便开发人员可以生成行为正常的应用程序。

概述

现代移动设备采用了以前需要专用设备(相机、音乐播放器和录音机)的功能。 正因为如此,多媒体框架成为了移动 API 中的一流功能。

Android 为多媒体提供了广泛的支持。 本文探讨了在 Android 中使用音频,并涵盖以下主题

  1. 使用 MediaPlayer 播放音频 - 使用内置的 MediaPlayer 类来播放音频,包括本地音频文件和采用 AudioTrack 类的流式传输音频文件。

  2. 录制音频 - 使用内置的 MediaRecorder 类来录制音频。

  3. 使用音频通知 - 使用音频通知创建行为良好的应用程序,通过暂停或取消其音频输出来正确响应事件(例如手机来电)。

  4. 使用低级别音频 - 通过直接写入内存缓冲区,使用 AudioTrack 类播放音频。 使用 AudioRecord 类录制音频,并从内存缓冲区直接读取。

要求

本指南需要 Android 2.0(API 级别 5)或更高版本。 请注意,必须在设备上调试 Android 上的音频。

需要在 AndroidManifest.XML 中请求 RECORD_AUDIO 权限:

启用 RECORD_AUDIO 的 Android Manifest 所需权限部分

使用 MediaPlayer 类播放音频

要在 Android 中播放音频,最简单的方法是使用内置的 MediaPlayer 类。 MediaPlayer 可以通过传入文件路径来播放本地或远程文件。 但是,MediaPlayer 对状态非常敏感,在错误的状态下调用它的一个方法将导致引发异常。 为避免错误,请务必按照下面所述的顺序与 MediaPlayer 进行交互。

初始化和播放

要使用 MediaPlayer 播放音频,需要以下序列:

  1. 实例化新的 MediaPlayer 对象。

  2. 将文件配置为通过 SetDataSource 方法播放。

  3. 调用 Prepare 方法来初始化播放器。

  4. 调用 Start 方法来启动音频播放。

下面的代码示例演示了这种用法:

protected MediaPlayer player;
public void StartPlayer(String  filePath)
{
  if (player == null) {
    player = new MediaPlayer();
  } else {
    player.Reset();
    player.SetDataSource(filePath);
    player.Prepare();
    player.Start();
  }
}

暂停和恢复播放

可通过调用 Pause 方法暂停播放:

player.Pause();

若要恢复暂停播放,请调用 Start 方法。 这将从播放中的暂停位置恢复:

player.Start();

在播放器上调用 Stop 方法将结束正在进行的播放:

player.Stop();

不再需要播放器时,必须调用 Release 方法来释放资源:

player.Release();

使用 MediaRecorder 类录制音频

要在 Android 中录制音频,MediaPlayer 一定会使用 MediaRecorder 类。 与 MediaPlayer 一样,它对状态敏感,并通过几个状态转换到可以开始录制的位置。 若要录制音频,必须设置 RECORD_AUDIO 权限。 有关如何设置应用程序权限的说明,请参阅使用 AndroidManifest.xml

初始化和录制

要使用 MediaRecorder 录制音频,需要执行以下步骤:

  1. 实例化新的 MediaRecorder 对象。

  2. 指定使用哪个硬件设备来通过 SetAudioSource 方法捕获音频输入。

  3. 使用 SetOutputFormat 方法设置输出文件音频格式。 有关支持的音频类型列表,请参阅 Android 支持的媒体格式

  4. 调用 SetAudioEncoder 方法来设置音频编码类型。

  5. 调用 SetOutputFile 方法来指定将音频数据写入到的输出文件的名称。

  6. 调用 Prepare 方法来初始化录音机。

  7. 调用 Start 方法开始录制。

以下代码示例演示了此序列:

protected MediaRecorder recorder;
void RecordAudio (String filePath)
{
  try {
    if (File.Exists (filePath)) {
      File.Delete (filePath);
    }
    if (recorder == null) {
      recorder = new MediaRecorder (); // Initial state.
    } else {
      recorder.Reset ();
      recorder.SetAudioSource (AudioSource.Mic);
      recorder.SetOutputFormat (OutputFormat.ThreeGpp);
      recorder.SetAudioEncoder (AudioEncoder.AmrNb);
      // Initialized state.
      recorder.SetOutputFile (filePath);
      // DataSourceConfigured state.
      recorder.Prepare (); // Prepared state
      recorder.Start (); // Recording state.
    }
  } catch (Exception ex) {
    Console.Out.WriteLine( ex.StackTrace);
  }
}

停止录制

若要停止录制,请对 MediaRecorder 调用 Stop 方法:

recorder.Stop();

清理

MediaRecorder 停止后,调用 Reset 方法将其重新置于其空闲状态:

recorder.Reset();

不再需要 MediaRecorder 时,必须调用 Release 方法来释放其资源:

recorder.Release();

管理音频通知

AudioManager 类

AudioManager 类提供对音频通知的访问权限,这些通知让应用程序知道何时发生音频事件。 此服务还提供对其他音频功能(如音量和响铃模式控制)的访问权限。 应用程序可使用 AudioManager 处理音频通知来控制音频播放。

管理音频焦点

设备(内置播放器和录音器)的音频资源由所有正在运行的应用程序共享。

从概念上讲,这类似于台式机上的应用程序,其中只有一个应用程序具有键盘焦点:在通过鼠标单击选择一个正在运行的应用程序后,键盘输入仅用于该应用程序。

音频焦点是一种类似的想法,可阻止多个应用程序同时播放或录制音频。 它比键盘焦点更复杂,因为它是自愿的(应用程序可以忽略它当前没有音频焦点的事实,并且可以不顾一切地播放),还因为可以请求不同类型的音频焦点。 例如,如果请求者只预期在很短的时间内播放音频,它可能会请求暂时的焦点。

可以立即授予音频焦点,也可以最初拒绝,稍后再授予。 例如,如果应用程序在电话通话期间请求音频焦点,请求会被拒绝,但在电话通话完成后,会授予焦点。 在这种情况下,会注册侦听器,以便在音频焦点被移走时做出相应的响应。 请求音频焦点用于确定是否可以播放或录制音频。

有关音频焦点的详细信息,请参阅管理音频焦点

注册音频焦点的回调

IOnAudioChangeListener 中注册 FocusChangeListener 回调是获取和释放音频焦点的重要部分。 这是因为授予音频焦点可能会延迟到稍后的时间。 例如,应用程序可能会在电话通话期间请求播放音乐。 在电话通话完成之前,不会授予音频焦点。

因此,回调对象作为参数传递到 AudioManagerGetAudioFocus 方法中,正是此调用注册了回调。 如果音频焦点最初被拒绝,但后来被授予,会通过在回调中调用 OnAudioFocusChange 来通知应用程序。 同样的方法用于告知应用程序音频焦点被移走。

当应用程序使用完音频资源后,它会调用 AudioManagerAbandonFocus 方法,并再次传入回调。 这会取消注册回调并释放音频资源,以便其他应用程序可以获取音频焦点。

请求音频焦点

要请求设备的音频资源,需要执行以下步骤:

  1. 获取 AudioManager 系统服务的句柄。

  2. 创建回调类的实例。

  3. 通过在 AudioManager 上调用 RequestAudioFocus 方法来请求设备的音频资源。 参数是回调对象、流类型(音乐、语音通话、响铃等)和所请求的访问权限类型(例如,可以暂时请求音频资源,也可无限期请求音频资源)。

  4. 如果请求被批准,会立即调用 playMusic 方法,并且开始播放音频。

  5. 如果请求被拒绝,则不采取进一步操作。 在这种情况下,音频只有在请求在稍后时间被批准后才会播放。

下面的代码示例显示了这些步骤:

Boolean RequestAudioResources(INotificationReceiver parent)
{
  AudioManager audioMan = (AudioManager) GetSystemService(Context.AudioService);
  AudioManager.IOnAudioFocusChangeListener listener  = new MyAudioListener(this);
  var ret = audioMan.RequestAudioFocus (listener, Stream.Music, AudioFocus.Gain );
  if (ret == AudioFocusRequest.Granted) {
    playMusic();
    return (true);
  } else if (ret == AudioFocusRequest.Failed) {
    return (false);
  }
  return (false);
}

释放音频焦点

当音轨播放完成时,会在 AudioManager 上调用 AbandonFocus 方法。 这样,另一个应用程序就可以获取设备的音频资源。 如果其他应用程序注册了自己的侦听器,它们将收到此音频焦点更改的通知。

低级别音频 API

低级别音频 API 可以更好地控制音频播放和录制,因为它们直接与内存缓冲区交互,而不是使用文件 URI。 在某些情况下,此方法更可取。 这些情况包括:

  1. 从加密的音频文件播放时。

  2. 播放一系列短片时。

  3. 音频流。

AudioTrack 类

AudioTrack 类使用低级别音频 API 进行录制,它是 MediaPlayer 类的低级别等效项。

初始化和播放

若要播放音频,必须实例化 AudioTrack 的新实例。 传入构造函数的参数列表指定了如何播放缓冲区中包含的音频示例。 参数包括:

  1. 流类型 - 语音、铃声、音乐、系统或警报。

  2. 频率 – 以 Hz 表示的采样率。

  3. 声道配置 - 单声道或立体声。

  4. 音频格式 - 8 位或 16 位编码。

  5. 缓冲区大小 - 以字节为单位。

  6. 缓冲区模式 - 流式处理或静态。

构造后,会调用 AudioTrackPlay 方法,将其设置为开始播放。 将音频缓冲区写入 AudioTrack 会开始播放:

void PlayAudioTrack(byte[] audioBuffer)
{
  AudioTrack audioTrack = new AudioTrack(
    // Stream type
    Stream.Music,
    // Frequency
    11025,
    // Mono or stereo
    ChannelOut.Mono,
    // Audio encoding
    Android.Media.Encoding.Pcm16bit,
    // Length of the audio clip.
    audioBuffer.Length,
    // Mode. Stream or static.
    AudioTrackMode.Stream);

    audioTrack.Play();
    audioTrack.Write(audioBuffer, 0, audioBuffer.Length);
}

暂停和停止播放

调用 Pause 方法以暂停播放:

audioTrack.Pause();

调用 Stop 方法将永久终止播放:

audioTrack.Stop();

清理

不再需要 AudioTrack 时,必须调用 Release 来释放其资源:

audioTrack.Release();

AudioRecord 类

AudioRecord 类在录制端等效于 AudioTrack。 与 AudioTrack 一样,它直接使用内存缓冲区来代替文件和 URI。 它要求在清单中设置 RECORD_AUDIO 权限。

初始化和录制

第一步是构造新的 AudioRecord 对象。 传入构造函数的参数列表提供录制所需的所有信息。 在 AudioTrack 中,参数基本上是枚举,而与之不同的是,AudioRecord 中的等效参数是整数。 这些设置包括:

  1. 硬件音频输入源,例如麦克风。

  2. 流类型 - 语音、铃声、音乐、系统或警报。

  3. 频率 – 以 Hz 表示的采样率。

  4. 声道配置 - 单声道或立体声。

  5. 音频格式 - 8 位或 16 位编码。

  6. 缓冲区大小 - 以字节为单位

构造 AudioRecord 后,会调用其 StartRecording 方法。 现在,它已准备好开始录制。 AudioRecord 持续读取输入的音频缓冲区,并将此输入写入音频文件。

void RecordAudio()
{
  byte[] audioBuffer = new byte[100000];
  var audRecorder = new AudioRecord(
    // Hardware source of recording.
    AudioSource.Mic,
    // Frequency
    11025,
    // Mono or stereo
    ChannelIn.Mono,
    // Audio encoding
    Android.Media.Encoding.Pcm16bit,
    // Length of the audio clip.
    audioBuffer.Length
  );
  audRecorder.StartRecording();
  while (true) {
    try
    {
      // Keep reading the buffer while there is audio input.
      audRecorder.Read(audioBuffer, 0, audioBuffer.Length);
      // Write out the audio file.
    } catch (Exception ex) {
      Console.Out.WriteLine(ex.Message);
      break;
    }
  }
}

停止录制

调用 Stop 方法将终止录制:

audRecorder.Stop();

清理

当不再需要 AudioRecord 对象时,调用它的 Release 方法会释放与其关联的所有资源:

audRecorder.Release();

总结

Android 操作系统提供了一个功能强大的框架,用于播放、录制和管理音频。 本文介绍了如何使用高级别 MediaPlayerMediaRecorder 类播放和录制音频。 接下来,它探索了如何使用音频通知在不同应用程序之间共享设备的音频资源。 最后,它介绍了如何使用低级别 API(与内存缓冲区直接连接)播放和录制音频。