A2B: Add A2B Tx streaming support

A2B: Add A2BDecoder for streaming wave to A2B device
RADA2B: Add functions to configure settings
This commit is contained in:
Yasser Yassine
2023-03-08 18:22:14 +00:00
parent 539cfa511b
commit ddee1254a0
12 changed files with 1145 additions and 523 deletions
+267 -34
View File
@@ -1,52 +1,285 @@
// libicsneo A2B example
// Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node
// Options:
// -h, --help Display help message.
// -e, --example [EXAMPLE_NUM] Example to run.
// Example usage: ./libicsneocpp-a2b.exe --example 1
// Example usage: ./libicsneocpp-a2b.exe -h
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include <fstream>
#include <icsneo/icsneocpp.h>
#include <icsneo/device/tree/rada2b/rada2bsettings.h>
#include <icsneo/communication/message/callback/streamoutput/a2bdecoder.h>
#include <string>
#include "icsneo/icsneocpp.h"
static constexpr size_t numFramesInWave = 48;
const std::string rada2bSerial = "Your RADA2B serial number.";
std::string makeWave() {
icsneo::WaveFileHeader header = icsneo::WaveFileHeader(1, 48000, 24);
std::vector<uint8_t> sineWaveSamples = {
0x00, 0x2B, 0x98, 0x08, 0x25, 0x01, 0x10, 0xD3, 0xEF, 0x18, 0x40, 0xA3, 0x20, 0x33, 0x4C, 0x26,
0xCE, 0xCA, 0x2D, 0x5B, 0x41, 0x32, 0xB9, 0x3C, 0x37, 0x6E, 0x50, 0x3B, 0x29, 0x18, 0x3D, 0xC2,
0x96, 0x3F, 0x86, 0xD3, 0x3F, 0xEC, 0x35, 0x3F, 0x85, 0xA7, 0x3D, 0xC4, 0x19, 0x3B, 0x28, 0xC9,
0x37, 0x6B, 0x5A, 0x32, 0xC1, 0xC4, 0x2D, 0x4A, 0xEE, 0x26, 0xE8, 0xB1, 0x20, 0x0E, 0xCF, 0x18,
0x6F, 0xAA, 0x10, 0x9C, 0x17, 0x08, 0x53, 0x35, 0x00, 0x01, 0xFD, 0xF7, 0xA9, 0x29, 0xEF, 0x66,
0x87, 0xE7, 0x8F, 0x37, 0xDF, 0xF0, 0x8A, 0xD9, 0x19, 0xB3, 0xD2, 0xB1, 0x3E, 0xCD, 0x42, 0xE9,
0xC8, 0x8F, 0xE0, 0xC4, 0xDB, 0x39, 0xC2, 0x39, 0x78, 0xC0, 0x7A, 0x8A, 0xC0, 0x16, 0x38, 0xC0,
0x74, 0x18, 0xC2, 0x44, 0xBF, 0xC4, 0xCE, 0x26, 0xC8, 0x9A, 0x99, 0xCD, 0x3F, 0x53, 0xD2, 0xA8,
0xBD, 0xD9, 0x32, 0xF5, 0xDF, 0xC2, 0x68, 0xE7, 0xD5, 0xFD, 0xEF, 0x02, 0x15, 0xF8, 0x3B, 0x33,
};
int main() {
std::cout << "Start example\n";
auto devices = icsneo::FindAllDevices();
std::vector<uint8_t> sineWave;
sineWave.reserve(sineWaveSamples.size() + sizeof(header));
std::shared_ptr<icsneo::Device> rada2b;
for(auto& device : devices) {
if(device->getSerial() == rada2bSerial) {
rada2b = device;
sineWave.insert(sineWave.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
std::copy(sineWaveSamples.begin(), sineWaveSamples.end(), std::back_inserter(sineWave));
return std::string(sineWave.begin(), sineWave.end());
}
// Example 0: TX
void example0(std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Transmitting a sine wave..." << std::endl;
// Create sine tone in wave format
std::string waveString = makeWave();
// Audio map to map which channel in wave to stream on a2b bus.
icsneo::A2BAudioChannelMap a2bmap(4);
a2bmap.set(
2, // Channel on a2b bus
icsneo::A2BMessage::A2BDirection::Downstream, // Direction
0 // Channel in wave file
);
a2bmap.setAll(0);
icsneo::A2BDecoder decoder(
std::make_unique<std::istringstream>(waveString), // Wave file stream
false, // True when using 16 bit samples
a2bmap
);
double elapsedTime = 0.0;
// Play roughly 5 seconds of sine tone.
while(elapsedTime < 5.0) {
decoder.outputAll(rada2b); // Output entire wave file
elapsedTime += ((double)numFramesInWave) * 1.0/48000.0;
decoder.stream->clear();
decoder.stream->seekg(0, std::ios::beg);
// Also outputs entire wave file
while(decoder && elapsedTime < 5.0) {
auto msg = decoder.decode();
rada2b->transmit(msg);
elapsedTime += ((double)msg->getNumFrames())*1.0/48000.0;
}
decoder.stream->clear();
decoder.stream->seekg(0, std::ios::beg);
}
rada2b->open();
rada2b->goOnline();
std::cout << rada2b->describe() << "\n";
}
auto handler1 = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("examples/cpp/a2b/src/out.wav")); // Starts writing A2B PCM data to out.wav
// Example 1: RX
void example1(std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Receiving 5 seconds of audio data..." << std::endl;
auto handler2 = rada2b->addMessageCallback(
std::make_shared<icsneo::MessageCallback>(
[](std::shared_ptr<icsneo::Message> message) {
std::cout << "Got in callback " << std::endl;
if(message->type == icsneo::Message::Type::Frame) {
std::shared_ptr<icsneo::Frame> frame = std::static_pointer_cast<icsneo::Frame>(message);
// Add WAV output message callback
// Saves samples to "out.wav"
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("out.wav", 48000));
if(frame->network.getType() == icsneo::Network::Type::A2B) {
std::shared_ptr<icsneo::A2BMessage> msg = std::static_pointer_cast<icsneo::A2BMessage>(frame);
std::cout << "Got A2B Message" << std::endl;
}
}
}));
// Sleep this thread for 5 seconds, message callback still runs
std::this_thread::sleep_for(std::chrono::seconds(5));
std::this_thread::sleep_for(std::chrono::seconds(5)); // captures 5 seconds of A2B data.
// Remove callback
rada2b->removeMessageCallback(handler);
}
rada2b->removeMessageCallback(handler1);
rada2b->removeMessageCallback(handler2);
std::cout << "End A2B example\n";
// Example 2: RADA2B settings
void example2(std::shared_ptr<icsneo::Device>& rada2b) {
uint32_t tdm;
{
// Get device settings
auto* settings = rada2b->settings.get();
auto* rada2bSettings = (icsneo::RADA2BSettings*)settings;
// Check if monitor mode is enabled
auto type = rada2bSettings->getNodeType(icsneo::RADA2BSettings::RADA2BDevice::Monitor);
if(type == icsneo::RADA2BSettings::NodeType::Monitor) {
std::cout << "Device is in monitor mode" << std::endl;
}
else {
std::cout << "Device is not in monitor mode" << std::endl;
}
// Get current tdm mode
tdm = rada2bSettings->getTDMModeInt(icsneo::RADA2BSettings::RADA2BDevice::Node);
std::cout << "Current tdm mode: " << tdm << std::endl;
// Set node type to master node.
rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master);
// Set TDM mode to TDM8
rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM8);
// Apply local settings to device
rada2bSettings->apply();
}
}
// Example 3: A2BMessage API
void example3() {
icsneo::A2BMessage msg = icsneo::A2BMessage(4, false, 2048); // Create new A2BMessage
msg[0][0] = 60; // Set sample using operator[][]
msg[0][3] = 60; // Frame 0, channel 2 upstream
msg[6][2] = 32; // Frame 6, channel 1 downstream
// Equivalent to last line
msg.setSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6, 32);
// Get sample
std::cout << "Channel 1 downstream sample for frame 6: " << msg.getSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6).value() << std::endl;
// Get number of frames
auto numFrames = msg.getNumFrames();
std::cout << "Num frames: " << numFrames << std::endl;
icsneo::A2BPCMSample sample1 = 40;
icsneo::A2BPCMSample sample2 = 60;
msg.fill(sample1); // Fill whole message with sample 40
msg.fillFrame(sample2, numFrames/2); // Fill frame numFrames/2 with sample2
// Print msg sample contents
std::cout << "A2B message contents:" << std::endl;
for(size_t y = 0; y < numFrames; y++) {
for(size_t x = 0; x < ((size_t)(msg.getNumChannels())*2); x++) { // Num channels including upstream and downstream
std::cout << msg[y][x] << " ";
}
std::cout << std::endl;
}
// Set and get bits
msg.setSyncFrameBit(true);
std::cout << "Was received from monitor: " << msg.isMonitorMsg() << std::endl;
}
void displayUsage() {
std::cout << "libicsneo A2B example" << std::endl;
std::cout << "Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << "-h, --help\tDisplay help message." << std::endl;
std::cout << "-e, --example [EXAMPLE_NUM]\tExample to run." << std::endl;
std::cout << "Example usage: ./libicsneocpp-a2b.exe --example 1" << std::endl;
std::cout << "Example usage: ./libicsneocpp-a2b.exe -h" << std::endl;
std::cout << std::endl;
std::cout << "Example options:" << std::endl;
std::cout << "0\ttx" << std::endl;
std::cout << "1\trx" << std::endl;
std::cout << "2\trx split channel" << std::endl;
std::cout << "3\tA2BMessage API" << std::endl;
}
int main(int argc, char **argv) {
std::vector<std::string> arguments(argv, argv + argc);
if(argc > 4 || argc == 1) {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
if(std::any_of(arguments.begin(), arguments.end(), [](const std::string& arg) { return arg == "-h" || arg == "--help"; })) {
displayUsage();
return EXIT_SUCCESS;
}
if(arguments[1] != "-e" && arguments[1] != "--example") {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
int option = atoi(arguments[2].c_str());
if(option < 0 || option > 3) {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
std::cout << icsneo::GetVersion() << std::endl;
const auto& devices = icsneo::FindAllDevices();
auto it = std::find_if(devices.begin(), devices.end(), [&](const auto& dev) {
const auto& txNetworks = dev->getSupportedTXNetworks();
const auto& rxNetworks = dev->getSupportedRXNetworks();
if(std::none_of(txNetworks.begin(), txNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
return false;
}
if(std::none_of(rxNetworks.begin(), rxNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
return false;
}
return true;
}
);
if(it == devices.end()) {
std::cerr << "Could not find RADA2B." << std::endl;
return EXIT_FAILURE;
}
std::shared_ptr<icsneo::Device> rada2b = *it;
if(!rada2b->open()) {
std::cout << "Failed to open RADA2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Opened RADA2B." << std::endl;
}
if(!rada2b->goOnline()) {
std::cout << "Failed to go online with RADA2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "RADA2B online." << std::endl;
}
switch(option) {
case 0:
example0(rada2b);
break;
case 1:
example1(rada2b);
break;
case 2:
example2(rada2b);
break;
case 3:
example3();
break;
default:
break;
}
rada2b->goOffline();
rada2b->close();
return 0;
}
return EXIT_SUCCESS;
}