mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
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:
@@ -0,0 +1,156 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bdecoder.h"
|
||||
#include <chrono>
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
static constexpr uint8_t maxChannel = 255;
|
||||
|
||||
|
||||
size_t A2BAudioChannelMap::getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const {
|
||||
size_t output = (size_t)channel;
|
||||
|
||||
if(dir == A2BMessage::A2BDirection::Upstream) {
|
||||
output++;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
A2BAudioChannelMap::A2BAudioChannelMap(uint8_t tdm) {
|
||||
rawMap.resize(2*tdm, maxChannel);
|
||||
}
|
||||
|
||||
void A2BAudioChannelMap::set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel) {
|
||||
auto index = getChannelIndex(outChannel, dir);
|
||||
rawMap[index] = inChannel;
|
||||
}
|
||||
|
||||
void A2BAudioChannelMap::setAll(Channel inChannel) {
|
||||
std::fill(rawMap.begin(), rawMap.end(), inChannel);
|
||||
}
|
||||
|
||||
Channel A2BAudioChannelMap::get(Channel outChannel, A2BMessage::A2BDirection dir) const {
|
||||
auto index = getChannelIndex(outChannel, dir);
|
||||
|
||||
return rawMap[index];
|
||||
}
|
||||
|
||||
size_t A2BAudioChannelMap::A2BAudioChannelMap::size() const {
|
||||
return rawMap.size();
|
||||
}
|
||||
|
||||
uint8_t A2BAudioChannelMap::getTDM() const {
|
||||
return (uint8_t)(rawMap.size() / 2);
|
||||
}
|
||||
|
||||
Channel& A2BAudioChannelMap::operator[](size_t idx) {
|
||||
return rawMap[idx];
|
||||
}
|
||||
|
||||
A2BAudioChannelMap::operator const std::vector<Channel>&() const {
|
||||
return rawMap;
|
||||
}
|
||||
|
||||
A2BDecoder::A2BDecoder(
|
||||
std::unique_ptr<std::istream>&& streamOut,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
) : channelSize16(chSize16), channelMap(chMap) {
|
||||
stream = std::move(streamOut);
|
||||
tdm = chMap.getTDM();
|
||||
initializeFromHeader();
|
||||
}
|
||||
|
||||
A2BDecoder::A2BDecoder(
|
||||
const char* filename,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
) : A2BDecoder(std::make_unique<std::ifstream>(filename, std::ios::binary), chSize16, chMap) { }
|
||||
|
||||
A2BDecoder::operator bool() const {
|
||||
return initialized && *stream;
|
||||
}
|
||||
|
||||
void A2BDecoder::initializeFromHeader() {
|
||||
WaveFileHeader header;
|
||||
if(!stream->read((char*)&header, sizeof(header))) {
|
||||
initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Only allow 16 or 24 bit samples
|
||||
if(header.bitsPerSample != 16 && header.bitsPerSample != 24) {
|
||||
initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
audioBytesPerSample = header.bitsPerSample == 16 ? 2 : 3;
|
||||
channelsInWave = (uint8_t)header.numChannels;
|
||||
|
||||
size_t bytesPerSample = channelSize16 ? 2 : 4;
|
||||
size_t frameSize = 2*tdm*bytesPerSample;
|
||||
size_t frameSizeWave = (size_t)(channelsInWave) * (size_t)(audioBytesPerSample);
|
||||
|
||||
frame.resize(frameSize, 0);
|
||||
frameWave.resize(frameSizeWave, 0);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<A2BMessage> A2BDecoder::decode() {
|
||||
if(!*(this)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto a2bMessagePtr = std::make_shared<icsneo::A2BMessage>(
|
||||
tdm,
|
||||
channelSize16,
|
||||
2048
|
||||
);
|
||||
|
||||
A2BMessage& a2bMessage = *a2bMessagePtr.get();
|
||||
|
||||
a2bMessage.setMonitorBit(false); // Probably not necessary
|
||||
a2bMessage.setTxMsgBit(true);
|
||||
|
||||
a2bMessage.network = Network(Network::NetID::A2B2);
|
||||
|
||||
for(uint32_t frameIndex = 0; frameIndex < a2bMessage.getNumFrames(); frameIndex++) {
|
||||
if(!stream->read((char*)frameWave.data(), frameWave.size())) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(size_t icsChannel = 0; icsChannel < channelMap.size(); icsChannel++) {
|
||||
|
||||
if(channelMap[icsChannel] >= maxChannel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t wBegin = audioBytesPerSample * channelMap[icsChannel];
|
||||
A2BPCMSample sample = 0;
|
||||
uint8_t* sampBytes = (uint8_t*)&sample;
|
||||
|
||||
std::copy(frameWave.begin() + wBegin, frameWave.begin() + wBegin + audioBytesPerSample, sampBytes);
|
||||
a2bMessage[frameIndex][icsChannel] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return a2bMessagePtr;
|
||||
}
|
||||
|
||||
bool A2BDecoder::outputAll(std::shared_ptr<Device>& device) {
|
||||
const auto& networks = device->getSupportedTXNetworks();
|
||||
|
||||
if(std::none_of(networks.begin(), networks.end(), [](const Network& net) { return net.getNetID() == Network::NetID::A2B2; })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(*this) {
|
||||
device->transmit(decode());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
|
||||
#include "icsneo/device/tree/rada2b/rada2b.h"
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
namespace icsneo {
|
||||
|
||||
void A2BWAVOutput::writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const {
|
||||
|
||||
WaveFileHeader header = WaveFileHeader(2 * firstMsg->getNumChannels(), wavSampleRate, firstMsg->getBitDepth());
|
||||
header.write(stream);
|
||||
streamStartPos = static_cast<uint32_t>(stream->tellp());
|
||||
|
||||
}
|
||||
|
||||
bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||
if(closed)
|
||||
{
|
||||
|
||||
if(closed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(message->type != Message::Type::Frame)
|
||||
if(message->type != Message::Type::Frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& frame = std::static_pointer_cast<Frame>(message);
|
||||
|
||||
@@ -31,23 +34,21 @@ bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||
firstMessageFlag = false;
|
||||
}
|
||||
|
||||
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::DownStream)) {
|
||||
close();
|
||||
|
||||
return false;
|
||||
// Might need to readd this block of code later if sample alignment fix is necessary
|
||||
/*
|
||||
std::streamsize bps = (std::streamsize)a2bmsg->getBytesPerSample();
|
||||
for(size_t i=0; i<a2bmsg->getNumSamples(); i++) {
|
||||
A2BPCMSample samp = *(a2bmsg->getSample(i));
|
||||
write((void*)&samp, bps);
|
||||
}
|
||||
*/
|
||||
|
||||
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::UpStream)) {
|
||||
close();
|
||||
|
||||
return false;
|
||||
}
|
||||
write((void*)a2bmsg->getAudioBuffer(), a2bmsg->getAudioBufferSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void A2BWAVOutput::close() const
|
||||
{
|
||||
void A2BWAVOutput::close() const {
|
||||
if(closed) {
|
||||
return;
|
||||
}
|
||||
@@ -65,36 +66,4 @@ void A2BWAVOutput::close() const
|
||||
closed = true;
|
||||
}
|
||||
|
||||
bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const
|
||||
{
|
||||
uint8_t numChannels = msg->getNumChannels();
|
||||
|
||||
uint8_t channel = 0;
|
||||
uint32_t frame = 0;
|
||||
uint8_t bitDepth = msg->getBitDepth();
|
||||
|
||||
while(true) {
|
||||
auto sample = msg->getSample(dir, channel, frame);
|
||||
|
||||
if(!sample) {
|
||||
if(channel == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t audioSample = sample.value() >> (32 - bitDepth);
|
||||
|
||||
write((void*)(&audioSample), A2BPCM_SAMPLE_SIZE);
|
||||
|
||||
channel = (channel + 1) % numChannels;
|
||||
if(channel == 0) {
|
||||
frame++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user