arduino-audio-tools
JupyterAudio.h
1 #pragma once
3 #include "AudioTools/CoreAudio/AudioStreams.h"
4 #include "AudioTools/CoreAudio/AudioOutput.h"
5 #include "AudioTools/AudioCodecs/CodecWAV.h"
6 #include <string.h>
7 #include <iostream>
8 #include <fstream>
9 #include <filesystem>
10 #include <stdio.h>
11 #include "nlohmann/json.hpp"
12 #include "xtl/xbase64.hpp"
13 
14 namespace audio_tools {
15 
19 class FileOutput : public Print {
20 public:
21  FileOutput(std::fstream &stream){
22  p_audio_stream = &stream;
23  }
24  size_t write(const uint8_t *data, size_t len) override {
25  p_audio_stream->write((const char*)data,len);
26  return len;
27  }
28  int availableForWrite() override {
29  return 1024;
30  }
31 protected:
32  std::fstream *p_audio_stream=nullptr;
33 };
34 
35 
40 template <typename T>
41 class ChartT {
42 public:
43  void setup(std::string fName, int channelCount, int channelNo) {
44  this->fname = fName;
45  this->channels = channelCount;
46  if (this->channels==0){
47  LOGE("Setting channels to 0");
48  }
49  this->channel = channelNo;
50  }
51 
52  int getChannels() {
53  return this->channels;
54  }
55 
56  int getChannel() {
57  return this->channel;
58  }
59 
61  const std::string chartData() {
62  str.clear();
63  str.str("");
64  // reset buffer;
65  if (channel<channels){
66  ifstream is;
67  is.open(fname, is.binary);
68  is.seekg(wav_header_size, is.beg);
69  std::list<int16_t> audioList;
70  T buffer[channels];
71  size_t rec_size = channels*sizeof(T);
72  while(is.read((char *)buffer, rec_size)){
73  audioList.push_back(transform(buffer[channel]));
74  }
75  string str_size = "102400"; //std::to_string(audioList.size());
76  str << "<style>div.x-svg {width: "<< str_size <<"px; }</style>";
77  str << "<div class='x-svg'><svg viewBox='0 0 "<< str_size << " 100'> <polyline fill='none' stroke='blue' stroke-width='1' points ='";
78  // copy data from input stream
79  size_t idx = 0;
80  for(int16_t sample: audioList){
81  str << idx++ << "," << sample << " ";
82  }
83  str << "'/></svg></div>";
84  } else {
85  str << "<p>Channel " << channel << " of " << channels << " does not exist!</p>";
86  }
87  return str.str();
88  }
89 
90 protected:
91  std::stringstream str;
92  std::string fname;
93  const int wav_header_size = 44;
94  int channels=0;
95  int channel=0;
96 
97  int transform(int x){
98  int result = x / 1000; // scale -32 to 32
99  result += 60; // shift down
100  return result;
101  }
102 };
103 
104 using Chart = ChartT<int16_t>;
105 
110 template <typename T>
111 class JupyterAudioT : public AudioStream {
112 public:
113 
114  JupyterAudioT(const char* fileName, AudioStream &stream, int bufferCount=20, int bufferSize=1024) {
115  buffer_count = bufferCount;
116  p_audio_stream = &stream;
117  cfg = stream.audioInfo();
118  copier.resize(bufferSize);
119  fname = fileName;
120  if (fileExists()){
121  remove(fileName);
122  }
123  }
124 
125  ChartT<T> &chart(int channel=0) {
126  createWAVFile();
127  assert(cfg.channels>0);
128  chrt.setup(fname, cfg.channels, channel);
129  return chrt;
130  }
131 
132  // provide the file name
133  const std::string &name() const {
134  return fname;
135  }
136 
137  // provides the absolute file path as string
138  const std::string path() const {
139  std::filesystem::path p = fname;
140  std::string result = std::filesystem::absolute(p);
141  return result;
142  }
143 
144  // fills a wav file with data once, the first time it was requested
145  void createWAVFile(){
146  try{
147  if (!fileExists()){
148  std::fstream fstream(fname, fstream.binary | fstream.trunc | fstream.out);
149  FileOutput fp(fstream);
150  wave_encoder.setAudioInfo(audioInfo());
151  out.setOutput(&fp);
152  out.setEncoder(&wave_encoder);
153  out.begin(); // output to decoder
154  copier.begin(out, *p_audio_stream);
155  copier.copyN(buffer_count);
156  fstream.close();
157  }
158  } catch(const std::exception& ex){
159  std::cerr << ex.what();
160  }
161  }
162 
163  bool fileExists() {
164  ifstream f(fname.c_str());
165  return f.good();
166  }
167 
168  int bufferCount(){
169  return buffer_count;
170  }
171 
172  // provides the wav data as bas64 encded string
173  std::string audio() {
174  std::ifstream fin(fname, std::ios::binary);
175  std::stringstream m_buffer;
176  m_buffer << fin.rdbuf();
177  return xtl::base64encode(m_buffer.str());
178  }
179 
180  // Provides the audion information
182  return cfg;
183  }
184 
185 protected:
186  AudioStream *p_audio_stream=nullptr;
187  ChartT<T> chrt;
188  WAVEncoder wave_encoder;
189  EncodedAudioOutput out;
190  StreamCopyT<T> copier;
191  AudioInfo cfg;
192  string fname;
193  size_t buffer_count=0;
194 };
195 
196 using JupyterAudio = JupyterAudioT<int16_t>;
197 
198 } // namespace audio_tools
199 
201 nl::json mime_bundle_repr(Chart &in) {
202  auto bundle = nl::json::object();
203  bundle["text/html"] = in.chartData();
204  return bundle;
205 }
206 
208 nl::json mime_bundle_repr(JupyterAudio &in) {
209  auto bundle = nl::json::object();
210  in.createWAVFile();
211  bundle["text/html"] = "<audio controls "
212  "src='data:audio/wav;base64," +
213  in.audio() + "'/>";
214  return bundle;
215 }
216 
If you want to use the framework w/o Arduino you need to provide the implementation of a couple of cl...
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: BaseStream.h:109
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition: BaseStream.h:140
Displays audio in a Jupyter as chart Just wrapps a stream to provide the chart data.
Definition: JupyterAudio.h:41
const std::string chartData()
Provides data as svg polyline.
Definition: JupyterAudio.h:61
A more natural Print class to process encoded data (aac, wav, mp3...). Just define the output and the...
Definition: AudioEncoded.h:21
bool begin() override
Starts the processing - sets the status to active.
Definition: AudioEncoded.h:136
void setOutput(Print &outputStream)
Defines/Changes the output target.
Definition: AudioEncoded.h:96
Simple layer for Print object to write to a c++ file.
Definition: JupyterAudio.h:19
Output to Jupyter. We write the data just to a file from where we can load the data again for differe...
Definition: JupyterAudio.h:111
AudioInfo audioInfo()
provides the actual input AudioInfo
Definition: JupyterAudio.h:181
Definition: NoArduino.h:58
Typed Stream Copy which supports the conversion from channel to 2 channels. We make sure that we allw...
Definition: StreamCopy.h:23
A simple WAV file encoder. If no AudioEncoderExt is specified the WAV file contains PCM data,...
Definition: CodecWAV.h:467
virtual void setAudioInfo(AudioInfo from) override
Update actual WAVAudioInfo.
Definition: CodecWAV.h:511
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:859
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:57