#pragma once #include "AudioTools/AudioLibs/Desktop/NoArduino.h" #include "AudioTools/CoreAudio/AudioStreams.h" #include "AudioTools/CoreAudio/AudioOutput.h" #include "AudioTools/AudioCodecs/CodecWAV.h" #include #include #include #include #include #include "nlohmann/json.hpp" #include "xtl/xbase64.hpp" namespace audio_tools { /** * @brief Simple layer for Print object to write to a c++ file */ class FileOutput : public Print { public: FileOutput(std::fstream &stream){ p_audio_stream = &stream; } size_t write(const uint8_t *data, size_t len) override { p_audio_stream->write((const char*)data,len); return len; } int availableForWrite() override { return 1024; } protected: std::fstream *p_audio_stream=nullptr; }; /** * @brief Displays audio in a Jupyter as chart * Just wrapps a stream to provide the chart data */ template class ChartT { public: void setup(std::string fName, int channelCount, int channelNo) { this->fname = fName; this->channels = channelCount; if (this->channels==0){ LOGE("Setting channels to 0"); } this->channel = channelNo; } int getChannels() { return this->channels; } int getChannel() { return this->channel; } /// Provides data as svg polyline const std::string chartData() { str.clear(); str.str(""); // reset buffer; if (channel audioList; T buffer[channels]; size_t rec_size = channels*sizeof(T); while(is.read((char *)buffer, rec_size)){ audioList.push_back(transform(buffer[channel])); } string str_size = "102400"; //std::to_string(audioList.size()); str << ""; str << "
"; } else { str << "

Channel " << channel << " of " << channels << " does not exist!

"; } return str.str(); } protected: std::stringstream str; std::string fname; const int wav_header_size = 44; int channels=0; int channel=0; int transform(int x){ int result = x / 1000; // scale -32 to 32 result += 60; // shift down return result; } }; /// @brief Default chart type for Jupyter integration /// @ingroup io using Chart = ChartT; /** * @brief Output to Jupyter. We write the data just to a file from where we can * load the data again for different representations. */ template class JupyterAudioT : public AudioStream { public: JupyterAudioT(const char* fileName, AudioStream &stream, int bufferCount=20, int bufferSize=1024) { buffer_count = bufferCount; p_audio_stream = &stream; cfg = stream.audioInfo(); copier.resize(bufferSize); fname = fileName; if (fileExists()){ remove(fileName); } } ChartT &chart(int channel=0) { createWAVFile(); assert(cfg.channels>0); chrt.setup(fname, cfg.channels, channel); return chrt; } // provide the file name const std::string &name() const { return fname; } // provides the absolute file path as string const std::string path() const { std::filesystem::path p = fname; std::string result = std::filesystem::absolute(p); return result; } // fills a wav file with data once, the first time it was requested void createWAVFile(){ try{ if (!fileExists()){ std::fstream fstream(fname, fstream.binary | fstream.trunc | fstream.out); FileOutput fp(fstream); wave_encoder.setAudioInfo(audioInfo()); out.setOutput(&fp); out.setEncoder(&wave_encoder); out.begin(); // output to decoder copier.begin(out, *p_audio_stream); copier.copyN(buffer_count); fstream.close(); } } catch(const std::exception& ex){ std::cerr << ex.what(); } } bool fileExists() { ifstream f(fname.c_str()); return f.good(); } int bufferCount(){ return buffer_count; } // provides the wav data as bas64 encded string std::string audio() { std::ifstream fin(fname, std::ios::binary); std::stringstream m_buffer; m_buffer << fin.rdbuf(); return xtl::base64encode(m_buffer.str()); } // Provides the audion information AudioInfo audioInfo() { return cfg; } protected: AudioStream *p_audio_stream=nullptr; ChartT chrt; WAVEncoder wave_encoder; EncodedAudioOutput out; StreamCopyT copier; AudioInfo cfg; string fname; size_t buffer_count=0; }; /// @brief Default Jupyter audio output with 16-bit samples /// @ingroup io using JupyterAudio = JupyterAudioT; } // namespace audio_tools /// Disply Chart in Jupyterlab xeus nl::json mime_bundle_repr(Chart &in) { auto bundle = nl::json::object(); bundle["text/html"] = in.chartData(); return bundle; } /// Disply Audio player in Jupyterlab xeus nl::json mime_bundle_repr(JupyterAudio &in) { auto bundle = nl::json::object(); in.createWAVFile(); bundle["text/html"] = "