Reads audio data from the specified OdinMediaStreamHandle
. This will return
audio data in the format specified when calling odin_startup_ex
or 48 kHz
interleaved by default (i.e. if you used odin_startup
to initialize the
SDK).
See our minimal example for an example of how to use
this function.
Declaration
OdinReturnCode odin_audio_read_data(OdinMediaStreamHandle stream,
float *out_buffer,
size_t out_buffer_len);
Parameters
stream
Handle of the media stream from which to read audio data. See OdinMediaStreamHandle
Declaration
OdinMediaStreamHandle stream;
out_buffer
Pointer to a buffer where the audio data will be stored.
out_buffer_len
The length of the buffer in samples. The buffer must be large enough to store the requested number of samples.
Returns
A return code indicating success or failure.
Discussion / Example
48Khz means that the audio data is sampled at 48,000 times per second meaning that in one second there are 48,000
samples
with interleaved float values (between -1 and 1). The best approach is to read samples every 20 milliseconds. This means
that we do read data 50 times per second (ie. 1 second is 1000 milliseconds, 1000 / 20 = 50). In each read, we will get
48,000 / 50 = 960 samples.
Here is an example from our NodeJS SDK:
Let's get started with some types. For each MediaAdded
event (i.e. a user added a new media stream), we will store the
media stream handle, the media stream id, and the peer id in a map.
Media types
struct Media {
OdinMediaStreamHandle Handle;
uint16_t Id;
uint64_t PeerId;
};
std::map<uint16_t, Media> _mediaStreams;
We also have a structure to store the audio samples which also has a method to convert float32 (-1 to 1) to float16.
AudioSamples structure
struct AudioSamples {
short Data[960];
float OriginalData[960];
size_t Len;
uint64_t PeerId;
uint16_t MediaId;
void ConvertFloat32ToFloat16(const float *in_buffer, size_t in_buffer_len, short *out_buffer) {
for (size_t i = 0; i < in_buffer_len; i++) {
float f = in_buffer[i];
if (f > 1.0f) {
f = 1.0f;
} else if (f < -1.0f) {
f = -1.0f;
}
int16_t f16 = (int16_t) (f * 32767.0f);
out_buffer[i] = f16;
}
}
void SetSamples(const float *samples, size_t len)
{
ConvertFloat32ToFloat16(samples, len, Data);
memcpy(OriginalData, samples, len * sizeof(float));
Len = len;
}
~AudioSamples()
{
}
};
While the NodeJS is running and connected to the room, we will read audio data from each media stream (i.e. from each
user connected to the room and having a microphone active) and send it to the NodeJS layer. We will use a buffer of 960
samples to store the audio data.
Read audio data from media streams
while (this->_running)
{
for (auto it = _mediaStreams.begin(); it != _mediaStreams.end(); it++)
{
OdinReturnCode rc = odin_audio_read_data((OdinMediaStreamHandle)it->second.Handle, this->_audioSamplesBuffer, 960);
if (odin_is_error(rc)) {
printf("Odin NodeJS Addon: Failed to read audio data\n");
break;
}
AudioSamples* samples = new AudioSamples();
samples->SetSamples(this->_audioSamplesBuffer, 960);
samples->PeerId = it->second.PeerId;
samples->MediaId = it->second.Id;
napi_status status = _audioDataReceivedEventListener.BlockingCall( samples, callback );
if ( status != napi_ok )
{
printf("Odin NodeJS Addon: Failed to call audio data received callback\n");
break;
}
}
std::this_thread::sleep_for( std::chrono::milliseconds ( 20 ) );
}