From 7af0d66c2270d2b804b029bf33d6d8b532625a74 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Tue, 17 Mar 2015 18:36:20 -0700 Subject: AudioRecord builder Add public and system APIs for building an AudioRecord instance. The validity of the combination of parameters is verified at "build" time. Bug 19699343 Change-Id: If959f0f35208fb81a902364aaeefc1ebef1a4d23 --- api/current.txt | 8 ++ api/system-current.txt | 10 ++ media/java/android/media/AudioRecord.java | 166 +++++++++++++++++++++++++++- media/java/android/media/MediaRecorder.java | 7 +- 4 files changed, 187 insertions(+), 4 deletions(-) diff --git a/api/current.txt b/api/current.txt index ce7a637c932e..33bdb0d79233 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14610,6 +14610,14 @@ package android.media { field public static final int SUCCESS = 0; // 0x0 } + public static class AudioRecord.Builder { + ctor public AudioRecord.Builder(); + method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException; + method public android.media.AudioRecord.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; + method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException; + method public android.media.AudioRecord.Builder setCapturePreset(int) throws java.lang.IllegalArgumentException; + } + public static abstract interface AudioRecord.OnRecordPositionUpdateListener { method public abstract void onMarkerReached(android.media.AudioRecord); method public abstract void onPeriodicNotification(android.media.AudioRecord); diff --git a/api/system-current.txt b/api/system-current.txt index 1d234e646280..1bbb67af472f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -15808,6 +15808,16 @@ package android.media { field public static final int SUCCESS = 0; // 0x0 } + public static class AudioRecord.Builder { + ctor public AudioRecord.Builder(); + method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException; + method public android.media.AudioRecord.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException; + method public android.media.AudioRecord.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; + method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException; + method public android.media.AudioRecord.Builder setCapturePreset(int) throws java.lang.IllegalArgumentException; + method public android.media.AudioRecord.Builder setSessionId(int) throws java.lang.IllegalArgumentException; + } + public static abstract interface AudioRecord.OnRecordPositionUpdateListener { method public abstract void onMarkerReached(android.media.AudioRecord); method public abstract void onPeriodicNotification(android.media.AudioRecord); diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 259fe37884e6..99b7bee562a4 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -20,6 +20,7 @@ import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.util.Iterator; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Binder; import android.os.Handler; @@ -313,8 +314,14 @@ public class AudioRecord audioParamCheck(attributes.getCapturePreset(), rate, encoding); - mChannelCount = AudioFormat.channelCountFromInChannelMask(format.getChannelMask()); - mChannelMask = getChannelMaskFromLegacyConfig(format.getChannelMask(), false); + int channelMask = AudioFormat.CHANNEL_IN_DEFAULT; + if ((format.getPropertySetMask() + & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) + { + channelMask = format.getChannelMask(); + } + mChannelCount = AudioFormat.channelCountFromInChannelMask(channelMask); + mChannelMask = getChannelMaskFromLegacyConfig(channelMask, false); audioBuffSizeCheck(bufferSizeInBytes); @@ -335,6 +342,161 @@ public class AudioRecord mState = STATE_INITIALIZED; } + /** + * Builder class for {@link AudioRecord} objects. + * Use this class to configure and create an AudioRecord instance. By setting the + * recording preset (a.k.a. recording source) and audio format parameters, you indicate which of + * those vary from the default behavior on the device. + *

Here is an example where Builder is used to specify all {@link AudioFormat} + * parameters, to be used by a new AudioRecord instance: + * + *

+     * AudioRecord recorder = new AudioRecord.Builder()
+     *         .setCapturePreset(MediaRecorder.AudioSource.VOICE_COMMUNICATION)
+     *         .setAudioFormat(new AudioFormat.Builder()
+     *                 .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+     *                 .setSampleRate(32000)
+     *                 .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+     *                 .build())
+     *         .setBufferSize(2*minBuffSize)
+     *         .build();
+     * 
+ *

+ * If the capture preset is not set with {@link #setCapturePreset(int)}, + * {@link MediaRecorder.AudioSource#DEFAULT} is used. + *
If the audio format is not specified or is incomplete, its sample rate will be the + * default output sample rate of the device (see + * {@link AudioManager#PROPERTY_OUTPUT_SAMPLE_RATE}), its channel configuration will be + * {@link AudioFormat#CHANNEL_IN_DEFAULT}. + *
Failing to set an adequate buffer size with {@link #setBufferSizeInBytes(int)} will + * prevent the successful creation of an AudioRecord instance. + */ + public static class Builder { + private AudioAttributes mAttributes; + private AudioFormat mFormat; + private int mBufferSizeInBytes; + private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE; + + /** + * Constructs a new Builder with the default values as described above. + */ + public Builder() { + } + + /** + * @param preset the capture preset (also referred to as the recording source). + * See {@link MediaRecorder.AudioSource} for the supported capture preset definitions. + * @return the same Builder instance. + * @throws IllegalArgumentException + */ + public Builder setCapturePreset(int preset) throws IllegalArgumentException { + if ( (preset < MediaRecorder.AudioSource.DEFAULT) || + (preset > MediaRecorder.getAudioSourceMax()) ) { + throw new IllegalArgumentException("Invalid audio source " + preset); + } + mAttributes = new AudioAttributes.Builder() + .setInternalCapturePreset(preset) + .build(); + return this; + } + + /** + * @hide + * To be only used by system components. Allows specifying non-public capture presets + * @param attributes a non-null {@link AudioAttributes} instance that contains the capture + * preset to be used. + * @return the same Builder instance. + * @throws IllegalArgumentException + */ + @SystemApi + public Builder setAudioAttributes(@NonNull AudioAttributes attributes) + throws IllegalArgumentException { + if (attributes == null) { + throw new IllegalArgumentException("Illegal null AudioAttributes argument"); + } + if (attributes.getCapturePreset() == MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID) { + throw new IllegalArgumentException( + "No valid capture preset in AudioAttributes argument"); + } + // keep reference, we only copy the data when building + mAttributes = attributes; + return this; + } + + /** + * Sets the format of the audio data to be captured. + * @param format a non-null {@link AudioFormat} instance + * @return the same Builder instance. + * @throws IllegalArgumentException + */ + public Builder setAudioFormat(@NonNull AudioFormat format) throws IllegalArgumentException { + if (format == null) { + throw new IllegalArgumentException("Illegal null AudioFormat argument"); + } + // keep reference, we only copy the data when building + mFormat = format; + return this; + } + + /** + * Sets the total size (in bytes) of the buffer where audio data is written + * during the recording. New audio data can be read from this buffer in smaller chunks + * than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum + * required buffer size for the successful creation of an AudioRecord instance. + * Using values smaller than getMinBufferSize() will result in an initialization failure. + * @param bufferSizeInBytes a value strictly greater than 0 + * @return the same Builder instance. + * @throws IllegalArgumentException + */ + public Builder setBufferSizeInBytes(int bufferSizeInBytes) throws IllegalArgumentException { + if (bufferSizeInBytes <= 0) { + throw new IllegalArgumentException("Invalid buffer size " + bufferSizeInBytes); + } + mBufferSizeInBytes = bufferSizeInBytes; + return this; + } + + /** + * @hide + * To be only used by system components. + * @param sessionId ID of audio session the AudioRecord must be attached to, or + * {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at + * construction time. + * @return the same Builder instance. + * @throws IllegalArgumentException + */ + @SystemApi + public Builder setSessionId(int sessionId) throws IllegalArgumentException { + if (sessionId < 0) { + throw new IllegalArgumentException("Invalid session ID " + sessionId); + } + mSessionId = sessionId; + return this; + } + + /** + * @return a new {@link AudioRecord} instance initialized with all the parameters set + * on this Builder + * @throws UnsupportedOperationException if the parameters set on the Builder + * were incompatible, or if they are not supported by the device. + */ + public AudioRecord build() throws UnsupportedOperationException { + if (mFormat == null) { + mFormat = new AudioFormat.Builder().build(); + } + if (mAttributes == null) { + mAttributes = new AudioAttributes.Builder() + .setInternalCapturePreset(MediaRecorder.AudioSource.DEFAULT) + .build(); + } + try { + return new AudioRecord(mAttributes, mFormat, mBufferSizeInBytes, mSessionId); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException(e.getMessage()); + } + } + } + // Convenience method for the constructor's parameter checks. // This, getChannelMaskFromLegacyConfig and audioBuffSizeCheck are where constructor // IllegalArgumentException-s are thrown diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 58c86f20605a..058cfd263386 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -157,8 +157,11 @@ public class MediaRecorder } /** - * Defines the audio source. These constants are used with - * {@link MediaRecorder#setAudioSource(int)}. + * Defines the audio source. + * An audio source defines both a default physical source of audio signal, and a recording + * configuration; it's also known as a capture preset. These constants are for instance used + * in {@link MediaRecorder#setAudioSource(int)} or + * {@link AudioRecord.Builder#setCapturePreset(int)}. */ public final class AudioSource { -- cgit v1.2.3-59-g8ed1b