Skip to main content

Using MicrophoneReader

MicrophoneReader captures raw microphone audio as PCM samples. Use it when you need audio data directly instead of recognized text: voice volume meters, waveform displays, pitch or beat analysis, custom recording pipelines, local voice effects, or sending captured samples to your own processing code.

The component reads mono 16-bit PCM from the Android microphone. When enableRecord is enabled, the engine starts capturing audio and publishes the captured samples as frame data.

The component works as a frame buffer:

  • framePCM is a NativeShortBuffer that stores the PCM samples available for the current frame.
  • frameLength is the number of valid samples in framePCM for the current frame.
  • sampleRate is the microphone sample rate currently used by the engine.
  • If no audio was captured for a frame, frameLength is 0.
  • Read the component every update. The next frame replaces the current frame data.
  • Only read samples from index 0 to frameLength - 1. The buffer can have extra capacity, and old values outside frameLength are not part of the current frame.

Setup

  1. Add MicrophoneReader to a GameObject.
  2. Enable enableRecord when you want to capture microphone audio.
  3. During update, check frameLength.
  4. Read samples from framePCM or use getFramePCMArray() when you want a regular short[].

On Android, the app needs microphone permission. If permission has not been granted, the component asks for it when recording is enabled.

Example With Virtual Attributes

This version uses virtual attributes and calculates the peak absolute sample value for the current frame.

SpatialObject myObject = /* your object */ null;
MicrophoneReader mic = myObject.findComponent(MicrophoneReader.class);

if (mic != null) {
mic.enableRecord = true;
}

// Run this part every update.
if (mic != null && mic.enableRecord) {
int length = mic.frameLength;
NativeShortBuffer pcm = mic.framePCM;

if (pcm != null && length > 0) {
int peak = 0;
for (int i = 0; i < length; i++) {
int value = Math.abs(pcm.get(i));
if (value > peak) {
peak = value;
}
}

Terminal.log("Microphone peak: " + peak + " at " + mic.sampleRate + " Hz");
}
}

Example With Methods

Use methods directly when you prefer explicit calls or when you want a short[] copy of the current frame.

SpatialObject myObject = /* your object */ null;
MicrophoneReader mic = myObject.findComponent(MicrophoneReader.class);

if (mic != null) {
mic.setEnableRecord(true);
}

// Run this part every update.
if (mic != null && mic.isEnableRecord()) {
short[] samples = mic.getFramePCMArray();

if (samples.length > 0) {
long sum = 0;
for (int i = 0; i < samples.length; i++) {
sum += Math.abs(samples[i]);
}

long averageAmplitude = sum / samples.length;
Terminal.log("Average microphone amplitude: " + averageAmplitude);
}
}

Choosing framePCM or getFramePCMArray()

Use framePCM when you want to avoid allocating a new Java array every frame. This is better for frequent audio analysis.

Use getFramePCMArray() when convenience matters more than allocation cost. It returns a new array containing only the valid samples for the current frame.

Notes

  • frameLength counts samples, not bytes.
  • Samples are signed 16-bit PCM values, usually in the range -32768 to 32767.
  • The amount of samples per frame can vary depending on device timing and engine frame rate.
  • If recording is disabled, frameLength becomes 0 and no new microphone samples are published.
  • For speech-to-text, use SpeechRecognizer instead. MicrophoneReader provides raw audio, not words.