Audio

API Reference


The Audio API handles audio interfaces including play or listen supported formats.

A Legato device can use several audio interfaces. You choose the input and output interfaces to tie together. The Audio stream-related to a particular interface is represented with an 'Audio Stream Reference'.

You can create your own audio path by connecting several audio streams together using audio connectors.

An audio path can support more than two audio interfaces. You can have a basic output audio path of a voice call to connect the Modem Voice Received interface with the Speaker interface, and at the same time, the Modem Voice Received interface can be also connected to a Recorder Device interface.

IPC interfaces binding

All the functions of this API are provided by the audioService foundation service.

Here's a code sample binding to audio services:

bindings:
{
   clientExe.clientComponent.le_audio -> audioService.le_audio
}

Configure the Audio

The audio profile can be set with the le_audio_SetProfile() function. The pre-configured profiles are defined with le_audio_Profile_t enumeration type.

Warning
Ensure to check the list of supported audio profiles for your specific platform.

The audio profile in use can be retrieved with the le_audio_GetProfile() function.

Then, the following functions let you enable or disable the audio settings on the selected audio interface:

Open/Close an Audio Interface

The following functions let you select the desired interface:

  • le_audio_OpenMic(): returns an Audio Stream Reference of the analog audio signal coming from the microphone input.
  • le_audio_OpenSpeaker(): returns an Audio Stream Reference of the analog audio signal routed to the Speaker output.
  • le_audio_OpenUsbRx(): returns an Audio Stream Reference of the digitized audio signal coming from an external device connected via USB Audio Class.
  • le_audio_OpenUsbTx(): returns an Audio Stream Reference of the digitized audio signal routed to an external device connected via USB Audio Class.
  • le_audio_OpenPcmRx(): it returns an Audio Stream Reference of the digitized audio signal coming from an external device connected via the PCM interface.
  • le_audio_OpenPcmTx(): it returns an Audio Stream Reference of the digitized audio signal routed to an external device connected via the PCM interface.
  • le_audio_OpenI2sRx(): it returns an Audio Stream Reference of the digitized audio signal coming from an external device connected via the I2S interface.
  • le_audio_OpenI2sTx(): it returns an Audio Stream Reference of the digitized audio signal routed to an external device connected via the I2S interface.
  • le_audio_OpenModemVoiceRx(): returns an Audio Stream Reference of the digitized audio signal coming from a voice call. The audio format is negotiated with the network when the call is established.
  • le_audio_OpenModemVoiceTx(): returns an Audio Stream Reference of the digitized audio signal routed to a voice call. The audio format is negotiated with the network when the call is established.

Multiple users can own the same stream at the same time.

le_audio_GetDefaultPcmTimeSlot() can be called to get the default PCM time slot used on the current platform.

le_audio_GetDefaultI2sMode() can be called to get the default I2s channel mode slot used on the current platform.

Call le_audio_Close() to release it. If several users own the same, corresponding stream reference, the interface will close only after the last user releases the audio stream.

You can configure the PCM interface with the le_audio_SetPcmSamplingRate(), le_audio_SetPcmSamplingResolution() and le_audio_SetPcmCompanding() functions. This function must be called before activating an audio path with the PCM interface, in other words you must call this function before connecting the PCM Stream to a connector.

In addition, the le_audio_GetPcmSamplingRate(), le_audio_GetPcmSamplingResolution() and le_audio_GetPcmCompanding() functions allows you to retrieve the PCM interface configuration.

Control an Audio Stream

Once the users get an Audio Stream reference, they can control it with the following functions:

  • le_audio_SetGain(): allows the user to adjust the gain of an audio stream (0 means 'muted', 100 is the maximum gain value).
  • le_audio_GetGain(): allows the User to retrieve the gain of an audio stream (0 means 'muted', 100 is the maximum gain value).
  • le_audio_Mute(): allows the user to mute an audio stream.
  • le_audio_Unmute(): allows the user to unmute an audio stream.
Note
Multimedia (playback and recording) must be controled separatly from the main audio path (Microphone/Speaker, I2S, PCM, USB). Muting/Unmuting a multimedia is done by muting/unmuting the multimedia stream, and not the other connected stream. For example, in case of playback + voice on the speaker, if the user wants to mute all the audio outcoming to the speaker, it must mute both the Speaker stream and the playback stream.
The hardware may or may not support the full 0-100 resolution, and if you want to see what was actually set call le_audio_GetGain() after le_audio_SetGain() to get the real value.
Warning
Ensure to check the list of supported audio streams for these functions on your specific platform.

Create Audio connectors

You can create your own audio path by connecting several audio streams together.

le_audio_CreateConnector() function creates a reference to an audio connector.

You can tie an audio stream to a connector by calling the le_audio_Connect() function.

You can remove an audio stream from a connector by calling the le_audio_Disconnect() function.

When finished with it, delete it using the le_audio_DeleteConnector() function.

Playback and Recording

An audio file can be played to any active output interfaces.

You must open a player interface by calling:

  • le_audio_OpenPlayer(): returns an Audio Stream Reference for file playback. An audio file can be played on the local audio interface like Speaker, USB Tx, PCM Tx, I2S Tx or on the remote audio interface Modem Voice Tx depending the kind of connector (input or output) is tied to.

Audio file recording can be done from any active input interfaces.

You must open a "File Recording" interface by calling:

  • le_audio_OpenRecorder(): returns an Audio Stream Reference for file recording. The local audio interface like Microphone, USB Rx, PCM Rx, I2S Rx is recorded into an audio file; or the Modem Voice Rx remote audio interface is recorded into an audio file, depending the kind of connector (input or output) is tied to.
  • le_audio_PlayFile(): must be called to play a specified file. WAVE (Waveform audio file) and AMR (Adaptive Multi Rate) are supported. AMR is an audio compression format optimized for speech coding. Two codecs are supported:
  • AMR_NB (AMR Narrowband) codec that encodes narrowband (200–3400 Hz) signals at variable bit rates ranging from 4.75 to 12.2 kbit/s. It was adopted as the standard speech codec by 3GPP.
  • AMR-WB (AMR Wideband) is an ITU-T standard speech codec which improved speech quality due to a wider speech bandwidth of 50–7000 Hz.
  • le_audio_PlaySamples(): must be called to initiate a playback using an audio flow. A pipe has to be opened first, then the PCM samples are sent throught the opened pipe.

A play can be done only on a connected stream. For instance, the "I2S Tx", "Modem Voice Rx" and "Player" must be previously connected before playing a file.

A record can be done only on a connected stream. For instance, the "I2S Rx", "Modem Voice Tx" and "Recorder" must be previously connected before recording a file.

A PCM configuration must be set with:

An AMR configuration must be set with:

le_audio_Stop(): can be called to stop a play/record. The playback/record is stopped playing/recording, the read/write position indicator associated with the file stream is rewound to the beginning of the file. A new file can be played/recorded using le_audio_PlayFile()/le_audio_PlaySamples()/le_audio_RecordFile()/le_audio_GetSamples().

  • le_audio_Pause(): can be called to pause a play/record. The file playing/recording is put in hold, the read/write position indicator of the file is not moved.
  • le_audio_Resume(): can be called to resume a paused play/record. The file playing/recording continue at the file's position indicator held after the pause.

The function below is an example of how a file can be played to the Modem Voice Tx output interface. The audio of the voice call is redirected to the I2S interfaces. This function simply plays a voice prompt to the remote party when the voice call is established.

static void ConnectAudioAndPlayVoicePromptToRemoteParty
(
void
)
{
// I get the audio from the voice call.
// I get the I2S interface references.
// I create the audio connectors.
// The voice call's audio is redirected to I2S interfaces.
if (mdmRxAudioRef && mdmTxAudioRef && i2sTxRef && i2sRxRef &&
audioInputConnectorRef && audioOutputConnectorRef)
{
le_audio_Connect(audioInputConnectorRef, i2sRxRef);
le_audio_Connect(audioInputConnectorRef, mdmTxAudioRef);
le_audio_Connect(audioOutputConnectorRef, i2sTxRef);
le_audio_Connect(audioOutputConnectorRef, mdmRxAudioRef);
}
// I get the interface reference for file playback.
int fd = open("/myvoiceprompt.wav", O_RDONLY));
// The audio file is played on the input connector, the remote party can now hear the voice
// prompt.
if (fileAudioRef && audioInputConnectorRef)
{
le_audio_Connect(audioInputConnectorRef, fileAudioRef);
le_audio_PlayFile(fileAudioRef, fd);
}
}

The users have also the possibility to register a handler function to catch some media-related notifications such as errors or audio events.

le_audio_AddMediaHandler() function installs a handler for player/recorder stream notifications.

le_audio_RemoveMediaHandler() function removes the player/recorder handler function.

The code sample below shows how the User can install a file's event handler.

// Handler function for Stream Event Notifications.
static void MyMediaHandler
(
void* contextPtr
)
{
switch(event)
{
LE_INFO("File event is LE_AUDIO_MEDIA_ENDED.");
break;
LE_INFO("File event is LE_AUDIO_MEDIA_ERROR.");
break;
}
}
// I must install my audio media event handler as follows:
[...]
int myAudioFileFd = -1;
le_audio_StreamRef_t fileAudioRef = NULL;
le_audio_MediaHandlerRef_t mediaHandlerRef = NULL;
myAudioFileFd=open("/myAudioFile.wav", O_RDONLY);
fileAudioRef = le_audio_OpenPlayer();
LE_ERROR_IF((fileAudioRef==NULL), "le_audio_OpenPlayer returns NULL!");
mediaHandlerRef = le_audio_AddMediaHandler(fileAudioRef,
MyMediaEventHandler,
NULL);
LE_ERROR_IF((mediaHandlerRef==NULL), "AddMediaHandler returns NULL!");
le_audio_Connect(audioInputConnectorRef, fileAudioRef);
// Play the file
le_audio_Play(fileAudioRef, myAudioFileFd);
[...]

DTMF

The le_audio_PlayDtmf() function allows the application to play one or several DTMF on a playback stream. The duration and the pause of the DTMFs must also be specified with the input parameters.

The le_audio_PlaySignallingDtmf() function allows the application to ask the Mobile Network to generate on the remote audio party the DTMFs. Compared with le_audio_PlayDtmf(), le_audio_PlaySignallingDtmf() function may offer a better signal quality, but the the duration and the pause timings may be less accurate.

The application must register a handler function to detect incoming DTMF characters on a specific input audio stream. The le_audio_AddDtmfDetectorHandler() function installs a handler for DTMF detection.

The le_audio_RemoveDtmfDetectorHandler() function uninstalls the handler function.

The DTMFs are: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C, D. Not case sensitive.

Note
The DTMF decoding works only on an active audio path.

Code samples

The following two code samples show how to create different audio paths for an incoming voice call.

The first example has the audio path depending on an USB Audio handset. The ConnectVoiceCallAudio() function creates this audio path. A USB Audio handset plugged in and fully operationnal, will redirect the call audio to the handset; otherwise, it will redirect the call to the default microphone and speaker.

Use DisconnectVoiceCallAudio() to delete the audio path.

le_result_t ConnectVoiceCallAudio
(
le_audio_ConnectorRef_t* audioInputConnectorRefPtr, // [OUT] Input connector.
le_audio_ConnectorRef_t* audioOutputConnectorRefPtr, // [OUT] Output connector.
le_audio_StreamRef_t* mdmRxAudioRefPtr, // [OUT] Received voice call audio stream.
le_audio_StreamRef_t* mdmTxAudioRefPtr, // [OUT] Transmitted voice call audio stream.
le_audio_StreamRef_t* deviceRxAudioRefPtr, // [OUT] Received device audio stream.
le_audio_StreamRef_t* deviceTxAudioRefPtr // [OUT] Transmitted device audio stream.
)
{
// I get the audio from the voice call.
*mdmRxAudioRefPtr = le_audio_OpenModemVoiceRx();
*mdmTxAudioRefPtr = le_audio_OpenModemVoiceTx();
// If I cannot get the audio from the voice call, I return an error.
if ((*mdmRxAudioRefPtr == NULL) || (*mdmTxAudioRefPtr == NULL))
{
// I close the audio interfaces that have failed to open.
le_audio_Close(*mdmRxAudioRefPtr);
le_audio_Close(*mdmTxAudioRefPtr);
return LE_FAULT;
}
// I create the audio connectors.
*audioInputConnectorRefPtr = le_audio_CreateConnector();
*audioOutputConnectorRefPtr = le_audio_CreateConnector();
// I verify if my Audio USB handset is plugged and operational before trying to use it.
if(IsMyUSBHandsetPlugged() == TRUE)
{
// I can redirect the audio to my USB handset using linear PCM audio format (PCM 16bits @ 16KHz)
*deviceRxAudioRefPtr = le_audio_OpenUsbRx();
*deviceTxAudioRefPtr = le_audio_OpenUsbTx();
le_audio_Connect(*audioInputConnectorRefPtr, *deviceRxAudioRefPtr);
le_audio_Connect(*audioOutputConnectorRefPtr, *deviceTxAudioRefPtr);
}
else
{
// There is no USB Audio handset, I redirect the audio to the in-built Microphone and Speaker.
*deviceRxAudioRefPtr = le_audio_OpenMic();
*deviceTxAudioRefPtr = le_audio_OpenSpeaker();
le_audio_Connect(*audioInputConnectorRefPtr, *deviceRxAudioRefPtr);
le_audio_Connect(*audioOutputConnectorRefPtr, *deviceTxAudioRefPtr);
}
// I tie the audio from the voice call to the connectors.
le_audio_Connect (*audioInputConnectorRefPtr, *mdmTxAudioRefPtr);
le_audio_Connect (*audioOutputConnectorRefPtr, *mdmRxAudioRefPtr);
return LE_OK;
}
void DisconnectVoiceCallAudio
(
le_audio_ConnectorRef_t audioInputConnectorRef, // [IN] Input connector.
le_audio_ConnectorRef_t audioOutputConnectorRef, // [IN] Output connector.
le_audio_StreamRef_t mdmRxAudioRef, // [IN] Received voice call audio stream.
le_audio_StreamRef_t mdmTxAudioRef, // [IN] Transmitted voice call audio stream.
le_audio_StreamRef_t deviceRxAudioRef, // [IN] Received device audio stream.
le_audio_StreamRef_t deviceTxAudioRef // [IN] Transmitted device audio stream.
)
{
// The call is terminated, I can close its audio interfaces.
le_audio_Close(mdmRxAudioRef);
le_audio_Close(mdmTxAudioRef);
// I close all the device interfaces.
le_audio_Close(deviceRxAudioRef);
le_audio_Close(deviceTxAudioRef);
// I delete the Audio connector references.
le_audio_DeleteConnector(audioInputConnectorRef);
le_audio_DeleteConnector(audioOutputConnectorRef);
}

The next code sample uses specific functions to deal with a new 'Incoming call' event during a call already in progress.

If no call is in progress, use the ConnectVoiceCallAudio() function to redirect the call audio to the in-built Microphone and Speaker.

If a new call is incoming, and considered a high priority call, you must mute the audio of the first call, and then connect the new call to your current audio path. Use SwitchVoiceCallAudio() for these actions.

Warning
Duration and pause can be dependent on the platform and/or network capabilities.

When the high priority call terminates, you can return back to your previous call and reactivate its audio with the SwitchBackVoiceCallAudio() function.

le_result_t ConnectVoiceCallAudio
(
le_audio_ConnectorRef_t* audioInputConnectorRefPtr, // [OUT] Input connector.
le_audio_ConnectorRef_t* audioOutputConnectorRefPtr, // [OUT] Output connector.
le_audio_StreamRef_t* mdmRxAudioRefPtr, // [OUT] Received voice call audio stream.
le_audio_StreamRef_t* mdmTxAudioRefPtr, // [OUT] Transmitted voice call audio stream.
le_audio_StreamRef_t* micRefPtr, // [OUT] Microphone stream.
le_audio_StreamRef_t* speakerRefPtr // [OUT] Speaker stream.
)
{
*mdmRxAudioRefPtr = le_audio_OpenModemVoiceRx();
*mdmTxAudioRefPtr = le_audio_OpenModemVoiceTx();
// If I cannot get the audio from the voice call, I return an error.
if ((*mdmRxAudioRefPtr == NULL) || (*mdmTxAudioRefPtr == NULL))
{
// I close the audio interfaces that have failed to open.
le_audio_Close(*mdmRxAudioRefPtr);
le_audio_Close(*mdmTxAudioRefPtr);
return LE_FAULT;
}
*audioInputConnectorRefPtr = le_audio_CreateConnector();
*audioOutputConnectorRefPtr = le_audio_CreateConnector();
// Redirect audio to the in-built Microphone and Speaker.
*speakerRefPtr = le_audio_OpenSpeaker();
*micRefPtr = le_audio_OpenMic();
le_audio_Connect(*audioInputConnectorRefPtr, *micRefPtr);
le_audio_Connect (*audioInputConnectorRefPtr, *mdmTxAudioRefPtr);
le_audio_Connect(*audioOutputConnectorRefPtr, *speakerRefPtr);
le_audio_Connect (*audioOutputConnectorRefPtr, *mdmRxAudioRefPtr);
return LE_OK;
}
le_result_t SwitchVoiceCallAudio
(
le_audio_ConnectorRef_t audioInputConnectorRef, // [IN] Input connector.
le_audio_ConnectorRef_t audioOutputConnectorRef, // [IN] Output connector.
le_audio_StreamRef_t oldMdmRxAudioRef, // [IN] Received audio stream of the previous voice call.
le_audio_StreamRef_t oldMdmTxAudioRef, // [IN] Transmitted audio stream of the previous voice call.
le_audio_StreamRef_t* newMdmRxAudioRefPtr, // [OUT] Received audio stream of the new voice call.
le_audio_StreamRef_t* newMdmTxAudioRefPtr // [OUT] Transmitted audio stream of the new voice call.
)
{
if ((newMdmRxAudioRefPtr == NULL) ||
(newMdmTxAudioRefPtr == NULL))
{
}
*newMdmRxAudioRefPtr = le_audio_OpenModemVoiceRx();
*newMdmTxAudioRefPtr = le_audio_OpenModemVoiceTx();
// If I cannot get the audio from the voice call, I return an error.
if ((*newMdmRxAudioRefPtr == NULL) || (*newMdmTxAudioRefPtr == NULL))
{
// I close the audio interfaces that have failed to open.
le_audio_Close(*newMdmRxAudioRefPtr);
le_audio_Close(*newMdmTxAudioRefPtr);
return LE_FAULT;
}
// I mute the previous call.
le_audio_Mute(oldMdmRxAudioRef);
le_audio_Mute(oldMdmTxAudioRef);
// I connect the new incoming call.
le_audio_Connect (audioInputConnectorRef, *newMdmTxAudioRefPtr);
le_audio_Connect (audioOutputConnectorRef, *newMdmRxAudioRefPtr);
return LE_OK;
}
le_result_t SwitchBackVoiceCallAudio
(
le_audio_StreamRef_t oldMdmRxAudioRef, // [IN] Received audio stream of the previous voice call.
le_audio_StreamRef_t oldMdmTxAudioRef, // [IN] Transmitted audio stream of the previous voice call.
le_audio_StreamRef_t newMdmRxAudioRef, // [IN] Received audio stream of the new voice call.
le_audio_StreamRef_t newMdmTxAudioRef // [IN] Transmitted audio stream of the new voice call.
)
{
// I can delete the new call audio interfaces.
le_audio_Close(newMdmRxAudioRef);
le_audio_Close(newMdmTxAudioRef);
// I can re-open the previous call streaming.
if (le_audio_Unmute(oldMdmRxAudioRef) != LE_OK)
{
return LE_FAULT;
}
if (le_audio_Unmute(oldMdmTxAudioRef) != LE_OK)
{
return LE_FAULT;
}
return LE_OK;
}

Copyright (C) Sierra Wireless Inc. Use of this work is subject to license.