Speaker
Overview
Maverick AI glasses have a built-in speaker. The SDK handles it via the M2AudioService (accessed from Evs.audioService).
There are two ways to play audio on the glasses:
| Way | Use it for | API |
|---|---|---|
| Stream | Live audio: AI / text-to-speech replies, media, internet radio | openStream → write → endStream / closeStream |
| Sound resource | Short sounds you play again and again: chimes, alerts, UI feedback | M2AudioResource + playSound |
The SDK converts your audio to the glasses' own format (LC3) and sends it over BLE at real-time pace.
Streaming Audio
Open a stream with the format of the bytes you will pass to write:
| Input | Description |
|---|---|
M2AudioStreamInput.Pcm16(sampleRate, channels) |
Raw signed 16-bit little-endian PCM, mono or stereo, any sample rate |
M2AudioStreamInput.Encoded(format) |
Encoded audio - MP3, AAC, M4A or WAV. The SDK decodes it. |
M2AudioStreamInput.Lc3Frames |
Audio already in the glasses' LC3 format, forwarded as is |
M2AudioStreamInput.Url(url) |
An internet radio stream (ICY / HTTP MP3 or AAC). The SDK reads it itself - do not call write. |
Only one stream can be open at a time.
// Text-to-speech output: 24 kHz mono PCM
let audio = Evs.shared.audioService
audio.openStream(input: M2AudioStreamInput.Pcm16(sampleRate: 24000, channels: .mono))
audio.write(data: pcmChunk, offset: 0, size: pcmChunk.size) // call as audio arrives
// ...
audio.endStream() // let the tail finish playing
Ending a Stream
| Call | What it does |
|---|---|
endStream() |
Graceful end: plays the audio still buffered, then closes. Use after the last write. |
closeStream() |
Stops now and drops anything still buffered. Use to cancel. |
flush() |
Drops the buffered audio but keeps the stream open - for when the user interrupts the AI mid-sentence and a new reply starts right away. |
For Encoded streams you do not need endStream(): the SDK reports onAudioStreamFinished by itself when the decoder reaches the end of the input and the speaker has played it.
Stream Events
Evs.audioService.registerStreamListener(object : M2AudioService.IOnAudioStreamEvents {
override fun onAudioStreamChanged(input: M2AudioStreamInput?) { /* opened / closed */ }
override fun onAudioStreamStarted(input: M2AudioStreamInput) { /* first audio reached the glasses */ }
override fun onAudioStreamFinished(input: M2AudioStreamInput) { /* Encoded stream fully played */ }
override fun onAudioStreamError(error: M2AudioStreamError) { /* error.code, error.message */ }
override fun onStreamMetadata(title: String) { /* internet radio: now playing */ }
})
write returns false when the SDK rejects the input straight away (no stream open, wrong data for the format). getLastStreamError() returns the reason.
Sound Resources
A sound is uploaded to the glasses once and played from there, so replaying it costs no BLE traffic. Supported source formats: WAV, MP3, AAC, M4A, and LC3.
In-memory audio works too: M2AudioResource(bytes, M2AudioFormat.Mp3).
| Call | What it does |
|---|---|
playSound(audio, repeat) |
Plays the sound, uploading it first if needed |
playSoundAdvanced(audio, repeat, offsetMillis, durationMillis) |
Plays part of the sound |
stopSound(audio) |
Stops it |
releaseSound(audio) |
Frees its slot on the glasses. A persistent sound stays cached for next time. |
deleteSound(audio) |
Removes it from the glasses, cache included |
Pass persistent = true when creating the resource to keep the sound in the glasses' cache across sessions.
Volume
| Call | Description |
|---|---|
setVolume(volume) / getVolume() |
Speaker volume, 0-100. Default 80. |
setDigitalGain(gain) |
Extra software boost above the hardware maximum. 1.0 = off; up to about 4.0 (+12 dB). Costs CPU - use only as much as needed. |
Notes
- Close the stream when you are done - an open stream keeps the speaker on.
- With the Glasses Simulator, the audio plays on the PC speaker.
See Also
- Microphone - Capture audio from the glasses microphones
- Resources - Upload and cache resources on the glasses