Device: Refactor A2B APIs

* Removes features in `A2BMessage` class to support API for reading 16, 24, and 32 bit samples from A2B channels
* Re-organizes WAV receiving and transmitting code and API
* Creates API for mapping message channels to WAV channels and vice versa for transmitting and receiving
* Fixes `icsneo::Network::NetID::ExtendedData` VnetID bug for `icsneo::ExtendedDataMessage` decoding
* Creates RAD-A2B sequence chart example
* Fixes coremini uploading for certain devices in EEPROM by introducing `icsneo::Device::supportsEraseMemory`
This commit is contained in:
Yasser Yassine
2024-03-12 12:06:49 +00:00
committed by Kyle Schwarz
parent 06f6861130
commit cb22e622b3
33 changed files with 1014 additions and 947 deletions
@@ -1,85 +0,0 @@
#ifndef __A2BDECODER_H_
#define __A2BDECODER_H_
#ifdef __cplusplus
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/device/device.h"
namespace icsneo {
typedef uint8_t Channel;
class A2BAudioChannelMap {
public:
A2BAudioChannelMap(uint8_t tdm);
void set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel);
void setAll(Channel inChannel);
Channel get(Channel outChannel, A2BMessage::A2BDirection dir) const;
size_t size() const;
uint8_t getTDM() const;
Channel& operator[](size_t idx);
operator const std::vector<Channel>&() const;
private:
size_t getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const;
std::vector<Channel> rawMap;
};
class A2BDecoder {
public:
A2BDecoder(
std::unique_ptr<std::istream>&& streamOut,
bool chSize16,
const A2BAudioChannelMap& chMap
);
A2BDecoder(
const char* filename,
bool chSize16,
const A2BAudioChannelMap& chMap
);
operator bool() const;
std::shared_ptr<A2BMessage> decode();
bool outputAll(std::shared_ptr<Device> &device);
std::unique_ptr<std::istream> stream;
private:
void initializeFromHeader();
uint8_t tdm;
uint8_t audioBytesPerSample;
uint8_t channelsInWave;
bool channelSize16;
A2BAudioChannelMap channelMap;
std::vector<uint8_t> frame;
std::vector<uint8_t> frameWave;
bool initialized = false;
};
}
#endif // __cplusplus
#endif
@@ -9,33 +9,73 @@
namespace icsneo {
/**
* A message callback which injests A2BMessage PCM data and formats it into a WAV file
*/
class A2BWAVOutput : public StreamOutput {
public:
A2BWAVOutput(const char* filename, uint32_t sampleRate = 44100)
: StreamOutput(filename), wavSampleRate(sampleRate) {}
static constexpr size_t wavBufferSize = 1024 * 32;
A2BWAVOutput(std::unique_ptr<std::ostream>&& os, uint32_t sampleRate = 44100)
: StreamOutput(std::move(os)), wavSampleRate(sampleRate) {}
/**
* Creates a new A2BWAVOutput object
*
* @param filename Name of desired output WAV file
* @param channelMap A map which maps a channel in the output WAV file to a channel in received messages. See docs for specific channel format in messages
* @param bitDepth The size of the samples in the WAV file.
* @param numWAVChannels The number of channels in the output WAV file
* @param sampleRate The output WAV file sample rate
*/
A2BWAVOutput(
const char* filename,
const ChannelMap& channelMap,
PCMType bitDepth,
size_t numWAVChannels,
uint32_t sampleRate = 48000
);
void writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const;
/**
* Creates a new A2BWAVOutput object
*
* @param os A std::ostream object which represents this WAV file
* @param channelMap A map which maps a channel in the output WAV file to a channel in received messages. See docs for specific channel format in messages
* @param bitDepth The size of the samples in the WAV file.
* @param numWAVChannels The number of channels in the output WAV file
* @param sampleRate The output WAV file sample rate
*/
A2BWAVOutput(
std::ostream& os,
const ChannelMap& channelMap,
PCMType bitDepth,
size_t numWAVChannels,
uint32_t sampleRate = 48000
);
bool callIfMatch(const std::shared_ptr<Message>& message) const override;
void close() const;
~A2BWAVOutput() override {
if(!closed) {
close();
}
}
~A2BWAVOutput() override;
protected:
void close() const;
bool initialize();
uint32_t wavSampleRate;
/**
* Write and clear the current stored audio buffer
*/
bool writeCurrentBuffer() const;
mutable std::vector<uint8_t> wavBuffer; // A buffer which is used to cache PCM data to write to disk later
mutable size_t wavBufferOffset = 0; // Current offset in the above buffer, gets incremented as data is read into buffer
uint32_t wavSampleRate; // The output WAV sample rate
size_t bytesPerSampleWAV; // The number of bytes per sample in the output WAV file
size_t numChannelsWAV; // The number of channels in the output WAV file
ChannelMap chMap; // A map which maps a WAV channel to a A2BMessage channel
size_t maxMessageChannel; // The highest message channel in the above channel map, this variable is used for error checking
bool initialized = false;
mutable uint32_t streamStartPos;
mutable bool firstMessageFlag = true;
mutable bool closed = false;
};
}
@@ -14,33 +14,34 @@
namespace icsneo {
struct WaveFileHeader {
#pragma pack(push, 1)
struct WAVHeader {
static constexpr uint32_t WAVE_CHUNK_ID = 0x46464952; // "RIFF"
static constexpr uint32_t WAVE_FORMAT = 0x45564157; // "WAVE"
static constexpr uint32_t WAVE_SUBCHUNK1_ID = 0x20746d66; // "fmt "
static constexpr uint32_t WAVE_SUBCHUNK2_ID = 0x61746164; // "data"
static constexpr uint16_t WAVE_SUBCHUNK1_SIZE = 16;
static constexpr uint16_t WAVE_AUDIO_FORMAT_PCM = 1;
static constexpr uint32_t WAVE_DEFAULT_SIZE = 0; // Default size for streamed wav
static constexpr uint32_t WAV_CHUNK_ID = 0x46464952; // "RIFF"
static constexpr uint32_t WAV_FORMAT = 0x45564157; // "WAV"
static constexpr uint32_t WAV_SUBCHUNK1_ID = 0x20746d66; // "fmt "
static constexpr uint32_t WAV_SUBCHUNK2_ID = 0x61746164; // "data"
static constexpr uint16_t WAV_SUBCHUNK1_SIZE = 16;
static constexpr uint16_t WAV_AUDIO_FORMAT_PCM = 1;
static constexpr uint32_t WAV_DEFAULT_SIZE = 0; // Default size for streamed wav
uint32_t chunkId = WAVE_CHUNK_ID; // "RIFF"
uint32_t chunkSize = WAVE_DEFAULT_SIZE; // number of bytes to follow
uint32_t format = WAVE_FORMAT; // "WAVE"
uint32_t subchunk1Id = WAVE_SUBCHUNK1_ID; // "fmt "
uint32_t subchunk1Size = WAVE_SUBCHUNK1_SIZE; // number of bytes in *this* subchunk (always 16)
uint16_t audioFormat = WAVE_AUDIO_FORMAT_PCM; // 1 for PCM
uint32_t chunkId = WAV_CHUNK_ID; // "RIFF"
uint32_t chunkSize = WAV_DEFAULT_SIZE; // number of bytes to follow
uint32_t format = WAV_FORMAT; // "WAV"
uint32_t subchunk1Id = WAV_SUBCHUNK1_ID; // "fmt "
uint32_t subchunk1Size = WAV_SUBCHUNK1_SIZE; // number of bytes in *this* subchunk (always 16)
uint16_t audioFormat = WAV_AUDIO_FORMAT_PCM; // 1 for PCM
uint16_t numChannels; // number of channels
uint32_t sampleRate; // sample rate in Hz
uint32_t byteRate; // bytes per second of audio: sampleRate * numChannels * (bitsPerSample / 8)
uint16_t blockAlign; // alignment of each block in bytes: numChannels * (bitsPerSample / 8)
uint16_t bitsPerSample; // number of bits in each sample
uint32_t subchunk2Id = WAVE_SUBCHUNK2_ID; // "data"
uint32_t subchunk2Size = WAVE_DEFAULT_SIZE; // number of bytes to follow
uint32_t subchunk2Id = WAV_SUBCHUNK2_ID; // "data"
uint32_t subchunk2Size = WAV_DEFAULT_SIZE; // number of bytes to follow
WaveFileHeader() = default;
WAVHeader() = default;
WaveFileHeader(uint16_t nChannels, uint32_t sRate, uint16_t bps, uint32_t nSamples = 0) {
WAVHeader(uint16_t nChannels, uint32_t sRate, uint16_t bps, uint32_t nSamples = 0) {
setHeader(nChannels, sRate, bps, nSamples);
}
@@ -59,48 +60,82 @@ struct WaveFileHeader {
subchunk2Size = numSamples * numChannels * (bitsPerSample / 8);
chunkSize = subchunk2Size + 36;
}
};
void write(const std::unique_ptr<std::ostream>& stream) {
#pragma pack(pop)
stream->write(reinterpret_cast<const char*>(&chunkId), 4);
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
stream->write(reinterpret_cast<const char*>(&format), 4);
stream->write(reinterpret_cast<const char*>(&subchunk1Id), 4);
stream->write(reinterpret_cast<const char*>(&subchunk1Size), 4);
stream->write(reinterpret_cast<const char*>(&audioFormat), 2);
stream->write(reinterpret_cast<const char*>(&numChannels), 2);
stream->write(reinterpret_cast<const char*>(&sampleRate), 4);
stream->write(reinterpret_cast<const char*>(&byteRate), 4);
stream->write(reinterpret_cast<const char*>(&blockAlign), 2);
stream->write(reinterpret_cast<const char*>(&bitsPerSample), 2);
stream->write(reinterpret_cast<const char*>(&subchunk2Id), 4);
stream->write(reinterpret_cast<const char*>(&subchunk2Size), 4);
class IWAVStream {
private:
std::unique_ptr<std::istream, std::function<void(std::istream*)>> stream;
bool initialized = false;
public:
WAVHeader header;
IWAVStream(std::istream& WAVInput)
: stream(&WAVInput, [](std::istream*){}) {
if(initialize()) {
initialized = true;
}
}
IWAVStream(const char* filename)
: stream(new std::ifstream(filename, std::ios::in | std::ios::binary), std::default_delete<std::istream>()) {
if(initialize()) {
initialized = true;
}
}
bool initialize() {
return !(!stream->read(reinterpret_cast<char*>(&header), sizeof(WAVHeader)));
}
operator bool() const {
return initialized && stream && stream->good();
}
bool read(char* into, std::streamsize num) {
return !(!stream->read(into, num));
}
/**
* Set stream immediately after WAV header
*/
void reset() {
if(!(*this)) {
return;
}
stream->clear();
stream->seekg(sizeof(icsneo::WAVHeader), std::ios::beg);
}
};
class StreamOutput : public MessageCallback {
public:
StreamOutput(std::unique_ptr<std::ostream>&& os, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
: MessageCallback(cb, f), stream(std::move(os)) {}
StreamOutput(std::ostream& os, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
: MessageCallback(cb, f), stream(&os, [](std::ostream*){}) {}
StreamOutput(const char* filename, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
: MessageCallback(cb, f) {
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
}
:
MessageCallback(cb, f),
stream(
new std::ofstream(filename, std::ios::binary),
std::default_delete<std::ostream>()
) {}
StreamOutput(const char* filename) : MessageCallback([](std::shared_ptr<Message> msg) {}) {
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
}
StreamOutput(const char* filename) :
MessageCallback([](std::shared_ptr<Message> msg) {}),
stream(
new std::ofstream(filename, std::ios::binary),
std::default_delete<std::ostream>()
) {}
StreamOutput(std::unique_ptr<std::ostream>&& os) : MessageCallback([](std::shared_ptr<Message> msg) {}), stream(std::move(os)) {}
StreamOutput(std::ostream& os) : MessageCallback([](std::shared_ptr<Message> msg) {}), stream(&os, [](std::ostream*){}) {}
protected:
std::unique_ptr<std::ostream> stream;
void write(void* msg, std::streamsize size) const {
stream->write(reinterpret_cast<const char*>(msg), size);
}
std::unique_ptr<std::ostream, std::function<void(std::ostream*)>> stream;
};
}