From b3dec883d082cb6149c3e41ed36817658783080d Mon Sep 17 00:00:00 2001 From: pschatzmann Date: Mon, 28 Oct 2024 17:37:42 +0100 Subject: [PATCH] MetaDataFilter --- .../cross_band_handy_walkie_talkie.ino | 47 ++-- src/AudioTools/AudioCodecs/CodecMP3Helix.h | 18 +- .../CoreAudio/AudioMetaData/MetaDataFilter.h | 258 ++++++++++-------- 3 files changed, 163 insertions(+), 160 deletions(-) diff --git a/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino b/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino index e67d7aac0..e1e7a3e19 100644 --- a/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino +++ b/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino @@ -12,43 +12,38 @@ #include "SD.h" AudioInfo info(32000, 2, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); -StreamCopy copier(out, sound); // copies sound into i2s +SineWaveGenerator sineWave( + 32000); // subclass of SoundGenerator with max amplitude of 32000 +GeneratedSoundStream sound( + sineWave); // Stream generated from sine wave +DriverPins my_pins; +AudioBoard board(AudioDriverES8388, my_pins); +AudioBoardStream out(board); +StreamCopy copier(out, sound); // copies sound into i2s // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - while(!Serial); + while (!Serial); AudioLogger::instance().begin(Serial, AudioLogger::Info); - //LOGLEVEL_AUDIOKIT = AudioKitDebug; + + // sd pins: clk, miso, mosi,cs, + my_pins.addSPI(ESP32PinsSD{PinFunction::SD, 44, 42, 43, 2, SPI}); + // add i2c codec pins: scl, sda, port, frequency + my_pins.addI2C(PinFunction::CODEC, 35, 36); + // add i2s pins: mclk, bck, ws,data_out, data_in ,(port) + my_pins.addI2S(PinFunction::CODEC, 47, 21, 12, 14, 11); // start I2S Serial.println("starting I2S..."); auto config = out.defaultConfig(TX_MODE); config.copyFrom(info); - config.sd_active = false; - // i2c - config.pins.i2c_sda = 36; - config.pins.i2c_scl = 35; - // i2s - config.pin_mck = 47; - config.pin_bck = 21; - config.pin_ws = 12; - config.pin_data = 14; - config.pin_data_rx = 11; - - //config.sd_active = false; - config.pins.sd_cs = 2; - config.pins.sd_miso = 42; - config.pins.sd_mosi = 43; - config.pins.sd_clk = 44; - out.begin(config, false); + config.sd_active = true; + out.begin(config); // check SD drive - if(!SD.begin(config.pins.sd_cs)){ + if (!SD.begin(2)) { Serial.println("Card Mount Failed"); stop(); } @@ -59,6 +54,4 @@ void setup(void) { } // Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file +void loop() { copier.copy(); } \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/CodecMP3Helix.h b/src/AudioTools/AudioCodecs/CodecMP3Helix.h index 60936d9bd..3d7b4046a 100644 --- a/src/AudioTools/AudioCodecs/CodecMP3Helix.h +++ b/src/AudioTools/AudioCodecs/CodecMP3Helix.h @@ -22,7 +22,6 @@ class MP3DecoderHelix : public AudioDecoder { mp3 = new libhelix::MP3DecoderHelix(); if (mp3!=nullptr){ mp3->setReference(this); - filter.setDecoder(mp3); } else { LOGE("Not enough memory for libhelix"); } @@ -37,7 +36,6 @@ class MP3DecoderHelix : public AudioDecoder { mp3 = new libhelix::MP3DecoderHelix(); if (mp3!=nullptr){ mp3->setReference(this); - filter.setDecoder(mp3); } else { LOGE("Not enough memory for libhelix"); } @@ -56,7 +54,6 @@ class MP3DecoderHelix : public AudioDecoder { mp3 = new libhelix::MP3DecoderHelix(); if (mp3!=nullptr){ mp3->setReference(this); - filter.setDecoder(mp3); } else { LOGE("Not enough memory for libhelix"); } @@ -83,7 +80,6 @@ class MP3DecoderHelix : public AudioDecoder { if (mp3!=nullptr) { //mp3->setDelay(CODEC_DELAY_MS); mp3->begin(); - filter.begin(); } return true; } @@ -111,7 +107,7 @@ class MP3DecoderHelix : public AudioDecoder { size_t write(const uint8_t* data, size_t len) { LOGD("%s: %zu", LOG_METHOD, len); if (mp3==nullptr) return 0; - return use_filter ? filter.write((uint8_t*)data, len): mp3->write((uint8_t*)data, len); + return mp3->write((uint8_t*)data, len); } /// checks if the class is active @@ -146,16 +142,6 @@ class MP3DecoderHelix : public AudioDecoder { } } - /// Activates a filter that makes sure that helix does not get any metadata segments - void setFilterMetaData(bool filter){ - use_filter = filter; - } - - /// Check if the metadata filter is active - bool isFilterMetaData() { - return use_filter; - } - /// Provides the maximum frame size - this is allocated on the heap and you can reduce the heap size my minimizing this value size_t maxFrameSize() { return mp3->maxFrameSize(); @@ -177,8 +163,6 @@ class MP3DecoderHelix : public AudioDecoder { } protected: libhelix::MP3DecoderHelix *mp3=nullptr; - MetaDataFilter filter; - bool use_filter = false; }; diff --git a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h b/src/AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h index ca8085485..2c1be5784 100644 --- a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h +++ b/src/AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h @@ -1,128 +1,154 @@ #pragma once #include "AudioLogger.h" +#include "AudioTools/CoreAudio/AudioOutput.h" +#include "AudioTools/CoreAudio/Buffers.h" namespace audio_tools { /** - * @brief Class which filters out ID3v1 and ID3v2 Metadata and provides only the audio data to the decoder + * @brief Class which filters out ID3v1 and ID3v2 Metadata and provides only the + * audio data to the decoder * @ingroup metadata * @author Phil Schatzmann * @copyright GPLv3 */ -template -class MetaDataFilter { - public: - /// Default Constructor - MetaDataFilter() = default; - - /// Constructor which assigns the decoder - MetaDataFilter(Decoder *decoder){ - setDecoder(decoder); - } - - /// Defines the decoder to which we write the data - void setDecoder(Decoder *decoder){ - p_decoder = decoder; - } - - /// (Re)starts the processing - void begin() { - TRACED(); - start = 0; - } - - /// Writes the data to the decoder - size_t write(uint8_t* data, size_t len){ - TRACED(); - if (p_decoder==nullptr) return 0; - int pos=0; int meta_len=0; - if (findTag(data, len, pos, meta_len)){ - LOGD("pos: %d len: %d",pos, meta_len); - if (startwrite(data+start,pos); - } - - int start_idx2 = pos+meta_len; - int len2 = len-start_idx2; - if (start_idx2write(data+start_idx2,len2); - } else { - // ignore audio of next write - start = meta_len - len2; - } - } else { - // ignore start number of characters - if (start>=len){ - start -= len; - } else { - p_decoder->write(data+start,len-start); - start = 0; - } - } - return len; - } - - protected: - Decoder *p_decoder=nullptr; - enum MetaType {TAG, TAG_PLUS, ID3}; - int start = 0; - /// ID3 verion 2 TAG Header (10 bytes) - struct ID3v2 { - uint8_t header[3]; // ID3 - uint8_t version[2]; - uint8_t flags; - uint8_t size[4]; - } tagv2; - - /// determines if the data conatins a ID3v1 or ID3v2 tag - bool findTag(uint8_t* data, size_t len, int &pos_tag, int &meta_len){ - MetaType tag_type; - if (find((const char*)data, len, pos_tag, tag_type)){ - switch(tag_type){ - case TAG: - LOGD("TAG"); - meta_len = 128; - break; - case TAG_PLUS: - LOGD("TAG+"); - meta_len = 227; - break; - case ID3: - LOGD("ID3"); - memcpy(&tagv2, data+pos_tag, sizeof(ID3v2)); - meta_len = calcSizeID3v2(tagv2.size); - break; - - } - return true; - } - return false; - } - - // calculate the synch save size for ID3v2 - uint32_t calcSizeID3v2(uint8_t chars[4]) { - uint32_t byte0 = chars[0]; - uint32_t byte1 = chars[1]; - uint32_t byte2 = chars[2]; - uint32_t byte3 = chars[3]; - return byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; - } - - /// find the tag position in the string; - bool find(const char*str, size_t len, int &pos, MetaType &type){ - if (str==nullptr || len<=0) return false; - for (size_t j=0;j<=len-3;j++){ - if (str[j]=='T' && str[j+1]=='A' && str[j+2]=='G'){ - type = str[j+3]=='+' ? TAG_PLUS : TAG; - return true; - } else if (str[j]=='I' && str[j+1]=='D' && str[j+2]=='3'){ - type = ID3; - return true; - } - } - return false; - } +class MetaDataFilter : public AudioOutput { + public: + /// Default Constructor + MetaDataFilter() = default; + + /// Constructor which assigns the decoder + MetaDataFilter(Print &out) { setOutput(out); } + + /// Defines the decoder to which we write the data + void setOutput(Print &out) { p_out = &out; } + + /// (Re)starts the processing + bool begin() override { + TRACED(); + start = 0; + return true; + } + + /// Writes the data to the decoder + size_t write(const uint8_t *data, size_t len) override { + TRACEI(); + size_t result = len; + // prevent npe + if ((p_out == nullptr) || (data == nullptr) || (len == 0)) return 0; + + // find tag + int meta_len = 0; + if (findTag(data, len, metadata_range.from, meta_len)) { + current_pos = 0; + metadata_range.setLen(meta_len); + LOGI("ignoring metadata at pos: %d len: %d", metadata_range.from, + meta_len); + } + + // nothing to ignore + if (!metadata_range.isDefined()) { + return p_out->write(data, len); + } + + // ignore data in metadata range + SingleBuffer tmp(len); + for (int j = 0; j < len; j++) { + if (!metadata_range.inRange(current_pos)) { + tmp.write(data[j]); + } + current_pos++; + } + + // write partial data + if (tmp.available() > 0) p_out->write(tmp.data(), tmp.available()); + + // reset for next run + if (current_pos > metadata_range.to) { + current_pos = 0; + metadata_range.clear(); + } + + return result; + } + + protected: + Print *p_out = nullptr; + int current_pos = 0; + enum MetaType { TAG, TAG_PLUS, ID3 }; + int start = 0; + /// Metadata range + struct Range { + int from = -1; + int to = -1; + + bool inRange(int pos) { return pos >= from && pos < to; } + void setLen(int len) { to = from + len; } + + void clear() { + from = -1; + to = -1; + } + bool isDefined() { return from != -1; } + } metadata_range; + + /// ID3 verion 2 TAG Header (10 bytes) + struct ID3v2 { + uint8_t header[3]; // ID3 + uint8_t version[2]; + uint8_t flags; + uint8_t size[4]; + } tagv2; + + /// determines if the data conatins a ID3v1 or ID3v2 tag + bool findTag(const uint8_t *data, size_t len, int &pos_tag, int &meta_len) { + MetaType tag_type; + if (find((const char *)data, len, pos_tag, tag_type)) { + switch (tag_type) { + case TAG: + LOGD("TAG"); + meta_len = 128; + break; + case TAG_PLUS: + LOGD("TAG+"); + meta_len = 227; + break; + case ID3: + LOGD("ID3"); + memcpy(&tagv2, data + pos_tag, sizeof(ID3v2)); + meta_len = calcSizeID3v2(tagv2.size); + break; + } + return true; + } + return false; + } + + // calculate the synch save size for ID3v2 + uint32_t calcSizeID3v2(uint8_t chars[4]) { + uint32_t byte0 = chars[0]; + uint32_t byte1 = chars[1]; + uint32_t byte2 = chars[2]; + uint32_t byte3 = chars[3]; + return byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; + } + + /// find the tag position in the string; + bool find(const char *str, size_t len, int &pos, MetaType &type) { + if (str == nullptr || len <= 0) return false; + for (size_t j = 0; j <= len - 3; j++) { + if (str[j] == 'T' && str[j + 1] == 'A' && str[j + 2] == 'G') { + type = str[j + 3] == '+' ? TAG_PLUS : TAG; + pos = j; + return true; + } else if (str[j] == 'I' && str[j + 1] == 'D' && str[j + 2] == '3') { + type = ID3; + pos = j; + return true; + } + } + return false; + } }; -} \ No newline at end of file +} // namespace audio_tools \ No newline at end of file