Objectifs:
- Understand basis of river
- Create a simple recording interface that print the average of sample absolute value.
When you will create an application based on the river audio interface you need :
Include:
Include manager and interface node
#include <audio/river/river.hpp>
#include <audio/river/Manager.hpp>
#include <audio/river/Interface.hpp>
Initilize the River library:
We first need to initialize etk sub library (needed to select the log level of sub-libraries and file access abstraction
Now we will initilaize the river library. To do this We have 2 posibilities:
With a file:
river::init("DATA:configFileName.json");
With a json string:
static const std::string configurationRiver =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" }\n"
"}\n";
river::initString(configurationRiver);
For the example we select the second solution (faster to implement example and resource at the same position.
river::init / river::initString must be called only one time for all the application, this represent the hardware configuration. It is NOT dynamic
To understand the configuration file Please see River configuration file
This json is parsed by the {#ejson_mainpage_what} it contain some update like:
- Optionnal " in the name of element.
- The possibilities to remplace " with '.
Get the river interface manager:
An application can have many interface and only one Manager. And a process can contain many application.
Then, we will get the first application manager handle.
Note: You can get back the application handle when you create a new one with the same name.
Create your read interface:
Generic code:
interface = manager->createInput(48000,
std::vector<audio::channel>(),
ioName);
if(interface == nullptr) {
std::cout << "nullptr interface" << std::endl;
return -1;
}
Here we create an interface with:
- The frequency of 48000 Hz.
- The default Low level definition channel
- A data interface of 16 bits samples coded in [-32768..32767]
- Select input interaface name "microphone"
set data callback:
The best way to get data is to instanciate a simple callback. The callback is called when sample arrive and you have the nbChunk/frequency to process the data, otherwise you can generate error in data stream.
interface->setInputCallback(std::bind(&onDataReceived,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4,
std::placeholders::_5,
std::placeholders::_6,
&outputNode));
Callback inplementation:
Simply declare your function and do what you want inside.
void onDataReceived(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
uint32_t _frequency,
const std::vector<audio::channel>& _map,
std::cout << "[ERROR] call wrong type ... (need int16_t.float)" << std::endl;
return;
}
start and stop the stream:
interface->start();
std::this_thread::sleep_for(std::chrono::seconds(10));
interface->stop();
Remove interfaces:
Full Sample:
#include <audio/river/river.hpp>
#include <audio/river/Manager.hpp>
#include <audio/river/Interface.hpp>
#include <thread>
static const std::string configurationRiver =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" }\n"
"}\n";
void onDataReceived(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
uint32_t _frequency,
const std::vector<audio::channel>& _map,
std::cout << "[ERROR] call wrong type ... (need int16_t.float)" << std::endl;
return;
}
const int16_t* data = static_cast<const int16_t*>(_data);
for (size_t iii=0; iii<_nbChunk*_map.size(); ++iii) {
value += std::abs(data[iii]);
}
value /= (_nbChunk*_map.size());
std::cout << "Get data ... average=" << int32_t(value) << std::endl;
} else {
const float* data = static_cast<const float*>(_data);
float value = 0;
for (size_t iii=0; iii<_nbChunk*_map.size(); ++iii) {
value += std::abs(data[iii]);
}
value /= (_nbChunk*_map.size());
std::cout << "Get data ... average=" << float(value) << std::endl;
}
} else {
_outputNode->
fileWrite(_data, _map.size()*audio::getFormatBytes(_format), _nbChunk);
}
}
int main(int _argc, const char **_argv) {
std::string configFile;
std::string ioName="microphone";
std::string outputFileName = "";
for (int32_t iii=0; iii<_argc ; ++iii) {
std::string data = _argv[iii];
if ( data == "-h"
|| data == "--help") {
std::cout << "Help : " << std::endl;
std::cout << " --conf=xxx.json Input/output configuration" << std::endl;
std::cout << " --io=xxx name configuration input" << std::endl;
std::cout << " --file=yyy.raw File name to store data" << std::endl;
exit(0);
} else if (etk::start_with(data, "--conf=") == true) {
configFile = std::string(data.begin()+7, data.end());
std::cout << "Select config: " << configFile << std::endl;
} else if (etk::start_with(data, "--io=") == true) {
ioName = std::string(data.begin()+5, data.end());
std::cout << "Select io: " << ioName << std::endl;
} else if (etk::start_with(data, "--file=") == true) {
outputFileName = std::string(data.begin()+7, data.end());
std::cout << "Select output file name: " << outputFileName << std::endl;
}
}
if (configFile == "") {
audio::river::initString(configurationRiver);
} else {
audio::river::init(configFile);
}
interface = manager->createInput(48000,
std::vector<audio::channel>(),
ioName);
if(interface == nullptr) {
std::cout << "nullptr interface" << std::endl;
return -1;
}
if (outputFileName != "") {
outputNode.
setName(outputFileName);
}
interface->setInputCallback(std::bind(&onDataReceived,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4,
std::placeholders::_5,
std::placeholders::_6,
&outputNode));
interface->start();
std::this_thread::sleep_for(std::chrono::seconds(10));
interface->stop();
if (outputFileName != "") {
}
return 0;
}