RSeries astromech firmware
AudioPlayer.h
Go to the documentation of this file.
1 /*
2  * Audio.h
3  *
4  * Created on: Oct 26,2018
5  * Updated on: Feb 10,2021
6  * Author: Wolle (schreibfaul1) ¯\_(ツ)_/¯
7  */
8 
9 #if !defined(ESP32)
10 #error Only supports ESP32
11 #endif
12 
13 #ifndef AudioPlayer_h_
14 #define AudioPlayer_h_
15 #define FF_LFN_UNICODE 2
16 #include "Arduino.h"
17 #include "base64.h"
18 #include "SPI.h"
19 #include "SD.h"
20 #include "SD_MMC.h"
21 #include "SPIFFS.h"
22 #include "FS.h"
23 #include "FFat.h"
24 #include "WiFiClientSecure.h"
25 #include "driver/i2s.h"
26 
27 // extern __attribute__((weak)) void audio_info(const char*);
28 // extern __attribute__((weak)) void audio_id3data(const char*); //ID3 metadata
29 // extern __attribute__((weak)) void audio_id3image(File& file, const size_t pos, const size_t size); //ID3 metadata image
30 // extern __attribute__((weak)) void audio_eof_mp3(const char*); //end of mp3 file
31 // extern __attribute__((weak)) void audio_showstreamtitle(const char*);
32 // extern __attribute__((weak)) void audio_showstation(const char*);
33 // extern __attribute__((weak)) void audio_bitrate(const char*);
34 // extern __attribute__((weak)) void audio_commercial(const char*);
35 // extern __attribute__((weak)) void audio_icyurl(const char*);
36 // extern __attribute__((weak)) void audio_lasthost(const char*);
37 // extern __attribute__((weak)) void audio_eof_speech(const char*);
38 // extern __attribute__((weak)) void audio_eof_stream(const char*); // The webstream comes to an end
39 
40 //----------------------------------------------------------------------------------------------------------------------
41 
42 class AudioBuffer {
43 // AudioBuffer will be allocated in PSRAM, If PSRAM not available or has not enough space AudioBuffer will be
44 // allocated in FlashRAM with reduced size
45 //
46 // m_readPtr m_writePtr m_endPtr
47 // |<------dataLength------->|<------ writeSpace ----->|
48 // ▼ ▼ ▼
49 // ---------------------------------------------------------------------------------------------------------------
50 // | <--m_buffSize--> | <--m_resBuffSize --> |
51 // ---------------------------------------------------------------------------------------------------------------
52 // |<------freeSpace-------->| |<------freeSpace-------->|
53 //
54 //
55 //
56 // if the space between m_readPtr and buffend < 1600 bytes copy data from the beginning to resBuff
57 // so that the mp3 frame is always completed
58 //
59 // m_writePtr m_readPtr m_endPtr
60 // |<-------writeSpace-1 --->|<--dataLength-->|
61 // ▼ ▼ ▼
62 // ---------------------------------------------------------------------------------------------------------------
63 // | <--m_buffSize--> | <--m_resBuffSize --> |
64 // ---------------------------------------------------------------------------------------------------------------
65 // |<--- ------dataLength-- ------>|<-------freeSpace------->|
66 //
67 //
68 
69 public:
70  AudioBuffer(); // constructor
71  ~AudioBuffer(); // frees the buffer
72  size_t init(size_t bufSize = 0); // set default values
73  size_t freeSpace(); // number of free bytes to overwrite
74  size_t writeSpace(); // space fom writepointer to bufferend
75  size_t bufferFilled(); // returns the number of filled bytes
76  void bytesWritten(size_t bw); // update writepointer
77  void bytesWasRead(size_t br); // update readpointer
78  uint8_t* writePtr(); // returns the current writepointer
79  uint8_t* readPtr(); // returns the current readpointer
80  uint32_t getWritePos(); // write position relative to the beginning
81  uint32_t getReadPos(); // read position relative to the beginning
82  void resetBuffer(); // restore defaults
83 
84 protected:
85  const size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes
86  const size_t m_buffSizeRAM = 1600 * 5;
87  size_t m_buffSize = 0;
88  size_t m_freeSpace = 0;
89  size_t m_writeSpace = 0;
90  size_t m_dataLength = 0;
91  size_t m_resBuffSize = 1600; // reserved buffspace, >= one mp3 frame
92  uint8_t* m_buffer = NULL;
93  uint8_t* m_writePtr = NULL;
94  uint8_t* m_readPtr = NULL;
95  uint8_t* m_endPtr = NULL;
96  bool m_f_start = true;
97 };
98 //----------------------------------------------------------------------------------------------------------------------
99 
100 typedef void (*AudioSampleFilter)(unsigned numBits, unsigned numChannels, const int16_t* samples, unsigned sampleCount);
101 
102 class AudioPlayer : private AudioBuffer{
103 
104  AudioBuffer InBuff; // instance of input buffer
105 
106 public:
107  AudioPlayer(); // #99
108  ~AudioPlayer();
109  bool connecttoFS(fs::FS &fs, const char* file);
110  bool connecttoSD(const char* sdfile);
111  bool setFileLoop(bool input);//TEST loop
112  bool connecttohost(const char* host, const char* user = "", const char* pwd = "");
113  bool connecttospeech(const char* speech, const char* lang);
114  bool connecttonull();
115  void loop();
116  uint32_t getFileSize();
117  uint32_t getFilePos();
118  uint32_t getSampleRate();
119  uint8_t getBitsPerSample();
120  uint8_t getChannels();
121 
127  uint32_t getAudioFileDuration();
133  uint32_t getAudioCurrentTime();
134  bool setFilePos(uint32_t pos);
143  bool audioFileSeek(const int8_t speed);
144  bool setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN=I2S_PIN_NO_CHANGE);
145  void stopSong();
151  bool pauseResume();
152  void forceMono(bool m);
153  void setBalance(int8_t bal = 0);
154  void setVolume(uint8_t vol);
155  uint8_t getVolume();
156  inline uint8_t getDatamode(){return m_datamode;}
157  inline void setDatamode(uint8_t dm){m_datamode=dm;}
158  inline uint32_t streamavail() {if(m_f_ssl==false) return client.available(); else return clientsecure.available();}
159  bool isRunning() {return m_f_running;}
160  bool isNullStream() {return m_f_nullstream;}
161  bool isLocalFile() {return m_f_localfile;}
162  bool isWebStream() {return m_f_webstream;}
163  esp_err_t i2s_mclk_pin_select(const uint8_t pin);
164  uint32_t inBufferFilled(); // returns the number of stored bytes in the inputbuffer
165  uint32_t inBufferFree(); // returns the number of free bytes in the inputbuffer
166  void setTone(uint8_t l_type = 0, uint16_t l_freq = 0, uint8_t r_type = 0, uint16_t r_freq = 0);
167  void setInternalDAC(bool internalDAC);
168  void setI2SCommFMT_LSB(bool commFMT);
169 
170  inline void setSampleFilter(AudioSampleFilter filter)
171  {
172  sampleFilter = filter;
173  }
174 
175 private:
176  void reset(); // free buffers and set defaults
177  void initInBuff();
178  void processNullStream();
179  void processLocalFile();
180  void processWebStream();
181  int sendBytes(uint8_t* data, size_t len);
182  void compute_audioCurrentTime(int bd);
183  void printDecodeError(int r);
184  int readWaveHeader(uint8_t* data, size_t len);
185  int readID3Metadata(uint8_t* data, size_t len);
186  int readM4AContainer(uint8_t* data, size_t len);
187  bool setSampleRate(uint32_t hz);
188  bool setBitsPerSample(int bits);
189  bool setChannels(int channels);
190  bool playChunk();
191  bool playSample(int16_t sample[2]) ;
192  bool playI2Sremains();
193  int32_t Gain(int16_t s[2]);
194  bool fill_InputBuf();
195  void showstreamtitle(const char* ml);
196  void parsePlaylistData(const char* pd);
197  void parseAudioHeader(const char* ah);
198  bool parseContentType(const char* ct);
199  void processControlData(uint8_t b);
200  esp_err_t I2Sstart(uint8_t i2s_num);
201  esp_err_t I2Sstop(uint8_t i2s_num);
202  String urlencode(String str);
203  int16_t* IIR_filterChain(int16_t iir_in[2], bool clear = false);
204  void IIR_calculateCoefficients();
205 
206  // implement several function with respect to the index of string
207  bool startsWith (const char* base, const char* str) { return (strstr(base, str) - base) == 0;}
208  bool endsWith (const char* base, const char* str) {
209  int blen = strlen(base);
210  int slen = strlen(str);
211  return (blen >= slen) && (0 == strcmp(base + blen - slen, str));
212  }
213  int indexOf (const char* base, const char* str, int startIndex) {
214  int result;
215  int baselen = strlen(base);
216  if (strlen(str) > baselen || startIndex > baselen) result = -1;
217  else {
218  char* pos = strstr(base + startIndex, str);
219  if (pos == NULL) result = -1;
220  else result = pos - base;
221  }
222  return result;
223  }
224  int specialIndexOf (uint8_t* base, const char* str, int baselen){
225  int result; // seek for str in buffer or in header up to baselen, not nullterninated
226  if (strlen(str) > baselen) return -1;
227  for (int i = 0; i < baselen - strlen(str); i++){
228  result = i;
229  for (int j = 0; j < strlen(str); j++){
230  if (*(base + i + j) != *(str + j)){
231  result = -1;
232  break;
233  }
234  }
235  if (result >= 0) break;
236  }
237  return result;
238  }
239  size_t bigEndian(uint8_t* base, uint8_t numBytes){
240  size_t result = 0;
241  if(numBytes < 1 or numBytes > 4) return 0;
242  for (int i = 0; i < numBytes; i++) {
243  result += *(base + i) << (numBytes -i - 1) * 8;
244  }
245  return result;
246  }
247 
248  virtual void audio_info(const char *info) {}
249  virtual void audio_id3data(const char *info) {}
250  virtual void audio_id3image(File& file, const size_t pos, const size_t size) {}
251  virtual void audio_eof_mp3(const char *info) {}
252  virtual void audio_showstation(const char *info) {}
253  virtual void audio_showstreamtitle(const char *info) {}
254  virtual void audio_bitrate(const char *info) {}
255  virtual void audio_commercial(const char *info) {}
256  virtual void audio_icyurl(const char *info) {}
257  virtual void audio_lasthost(const char *info) {}
258  virtual void audio_eof_speech(const char *info) {}
259  virtual void audio_eof_stream(const char*) {}
260 
261 private:
262  enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
263  enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
264  enum : int { CODEC_NONE = 0, CODEC_WAV = 1, CODEC_MP3 = 2, CODEC_AAC = 4, CODEC_M4A = 5};
265  enum : int { FORMAT_NONE = 0, FORMAT_M3U = 1, FORMAT_PLS = 2, FORMAT_ASX = 3};
266  enum : int { AUDIO_NONE, AUDIO_HEADER , AUDIO_DATA, AUDIO_METADATA, AUDIO_PLAYLISTINIT,
267  AUDIO_PLAYLISTHEADER, AUDIO_PLAYLISTDATA, AUDIO_SWM };
268  typedef enum { LEFTCHANNEL=0, RIGHTCHANNEL=1 } SampleIndex;
269 
270  const uint8_t volumetable[22]={ 0, 1, 2, 3, 4 , 6 , 8, 10, 12, 14, 17,
271  20, 23, 27, 30 ,34, 38, 43 ,48, 52, 58, 64}; //22 elements
272 
273  typedef struct _filter{
274  float a0;
275  float a1;
276  float a2;
277  float b1;
278  float b2;
279  } filter_t;
280 
281  File audiofile; // @suppress("Abstract class cannot be instantiated")
282  WiFiClient client; // @suppress("Abstract class cannot be instantiated")
283  WiFiClientSecure clientsecure; // @suppress("Abstract class cannot be instantiated")
284  i2s_config_t m_i2s_config; // stores values for I2S driver
285  i2s_pin_config_t m_pin_config;
286  AudioSampleFilter sampleFilter = NULL;
287  unsigned dummy = 0;
288 
289  char chbuf[600];
290  char path[256];
291  char m_plsURL[256]; // URL found in playlist
292  char m_lastHost[256]; // Store the last URL to a webstream
293  char m_audioName[256]; // the name of the file
294  filter_t m_filter[2];
295  int m_id3Size=0; // length id3 tag
296  int m_LFcount = 0; // Detection of end of header
297  uint32_t m_sampleRate=16000;
298  int m_bytesLeft=0;
299  uint32_t m_bitRate=0; // current bitrate given fom decoder
300  uint32_t m_avr_bitrate = 0; // average bitrate, median computed by VBR
301  int m_readbytes=0; // bytes read
302  int m_metalen=0; // Number of bytes in metadata
303  int m_controlCounter = 0; // Status within readID3data() and readWaveHeader()
304  int8_t m_balance = 0; // -16 (mute left) ... +16 (mute right)
305  uint8_t m_ID3version=0; // revision, ID3 version
306  uint8_t m_vol=64; // volume
307  uint8_t m_bitsPerSample = 16; // bitsPerSample
308  uint8_t m_channels=2;
309  uint8_t m_i2s_num = I2S_NUM_0; // I2S_NUM_0 or I2S_NUM_1
310  uint8_t m_playlistFormat = 0; // M3U, PLS, ASX
311  uint8_t m_codec = CODEC_NONE; //
312  uint8_t m_filterType[2]; // lowpass, highpass
313  int16_t m_outBuff[2048*2]; // [1152 * 2]; // Interleaved L/R
314  int16_t m_validSamples = 0;
315  int16_t m_curSample = 0;
316  uint16_t m_st_remember = 0; // Save hash from the last streamtitle
317  uint16_t m_datamode = 0; // Statemaschine
318  uint32_t m_metaint = 0; // Number of databytes between metadata
319  uint32_t m_totalcount = 0; // Counter mp3 data
320  uint32_t m_chunkcount = 0 ; // Counter for chunked transfer
321  uint32_t m_t0 = 0; // store millis(), is needed for a small delay
322  uint32_t m_metaCount = 0; // Bytecounter between metadata
323  uint32_t m_contentlength = 0; // Stores the length if the stream comes from fileserver
324  uint32_t m_bytectr = 0; // count received data
325  uint32_t m_bytesNotDecoded = 0; // pictures or something else that comes with the stream
326  bool m_f_unsync = false; // set within ID3 tag but not used
327  bool m_f_exthdr = false; // ID3 extended header
328  bool m_f_localfile = false ; // Play from local mp3-file
329  bool m_f_webstream = false ; // Play from URL
330  bool m_f_nullstream = false ; // Play from null
331  bool m_f_ssl = false;
332  bool m_f_running = false;
333  bool m_f_firststream_ready = false; // Set after connecttohost and first streamdata are available
334  bool m_f_ctseen = false; // First line of header seen or not
335  bool m_f_chunked = false ; // Station provides chunked transfer
336  bool m_f_swm = false;
337  bool m_f_firstmetabyte = false; // True if first metabyte (counter)
338  bool m_f_stream = false; // Set false if stream is lost
339  bool m_f_playing = false; // valid mp3 stream recognized
340  bool m_f_webfile= false; // assume it's a radiostream, not a podcast
341  bool m_f_psram = false; // set if PSRAM is availabe
342  bool m_f_loop = false; // Set if audio file should loop
343  bool m_f_forceMono = false; // if true stereo -> mono
344  bool m_f_internalDAC = false; // false: output vis I2S, true output via internal DAC
345  uint32_t m_audioFileDuration = 0;
346  float m_audioCurrentTime = 0;
347  float m_filterBuff[2][2][2]; // IIR filters memory for Audio DSP
348  size_t m_i2s_bytesWritten = 0; // set in i2s_write() but not used
349  size_t m_loop_point = 0; // Point in the file where the audio data starts
350  size_t m_file_size = 0; // size of the file
351  uint16_t m_filterFrequency[2];
352 };
353 
354 #endif /* AudioPlayer_h_ */
AudioPlayer::connecttonull
bool connecttonull()
AudioPlayer::inBufferFilled
uint32_t inBufferFilled()
AudioPlayer::i2s_mclk_pin_select
esp_err_t i2s_mclk_pin_select(const uint8_t pin)
AudioBuffer::writePtr
uint8_t * writePtr()
AudioPlayer::isNullStream
bool isNullStream()
Definition: AudioPlayer.h:160
AudioPlayer::getChannels
uint8_t getChannels()
AudioBuffer::m_buffer
uint8_t * m_buffer
Definition: AudioPlayer.h:92
AudioPlayer::pauseResume
bool pauseResume()
pauseResume pauses current playback
AudioBuffer::bufferFilled
size_t bufferFilled()
AudioPlayer
Definition: AudioPlayer.h:102
AudioPlayer::getFilePos
uint32_t getFilePos()
AudioPlayer::getFileSize
uint32_t getFileSize()
AudioPlayer::setFileLoop
bool setFileLoop(bool input)
AudioPlayer::getAudioCurrentTime
uint32_t getAudioCurrentTime()
Get the current plying time in seconds.
AudioPlayer::loop
void loop()
AudioPlayer::connecttoSD
bool connecttoSD(const char *sdfile)
AudioPlayer::setI2SCommFMT_LSB
void setI2SCommFMT_LSB(bool commFMT)
AudioSampleFilter
void(* AudioSampleFilter)(unsigned numBits, unsigned numChannels, const int16_t *samples, unsigned sampleCount)
Definition: AudioPlayer.h:100
AudioPlayer::connecttohost
bool connecttohost(const char *host, const char *user="", const char *pwd="")
AudioPlayer::connecttoFS
bool connecttoFS(fs::FS &fs, const char *file)
AudioBuffer::m_dataLength
size_t m_dataLength
Definition: AudioPlayer.h:90
AudioPlayer::inBufferFree
uint32_t inBufferFree()
AudioBuffer::m_buffSizePSRAM
const size_t m_buffSizePSRAM
Definition: AudioPlayer.h:85
AudioBuffer::writeSpace
size_t writeSpace()
AudioPlayer::getSampleRate
uint32_t getSampleRate()
AudioPlayer::AudioPlayer
AudioPlayer()
AudioPlayer::setFilePos
bool setFilePos(uint32_t pos)
AudioBuffer::m_buffSize
size_t m_buffSize
Definition: AudioPlayer.h:87
AudioPlayer::setInternalDAC
void setInternalDAC(bool internalDAC)
AudioPlayer::setSampleFilter
void setSampleFilter(AudioSampleFilter filter)
Definition: AudioPlayer.h:170
AudioBuffer::freeSpace
size_t freeSpace()
AudioBuffer::~AudioBuffer
~AudioBuffer()
AudioPlayer::setDatamode
void setDatamode(uint8_t dm)
Definition: AudioPlayer.h:157
AudioPlayer::isRunning
bool isRunning()
Definition: AudioPlayer.h:159
AudioPlayer::getDatamode
uint8_t getDatamode()
Definition: AudioPlayer.h:156
AudioPlayer::streamavail
uint32_t streamavail()
Definition: AudioPlayer.h:158
AudioBuffer::readPtr
uint8_t * readPtr()
AudioBuffer::m_f_start
bool m_f_start
Definition: AudioPlayer.h:96
AudioPlayer::~AudioPlayer
~AudioPlayer()
AudioPlayer::getAudioFileDuration
uint32_t getAudioFileDuration()
Get the audio file duration in seconds.
AudioBuffer::bytesWritten
void bytesWritten(size_t bw)
AudioPlayer::audioFileSeek
bool audioFileSeek(const int8_t speed)
audioFileSeek seeks the file in both directions
AudioBuffer::m_buffSizeRAM
const size_t m_buffSizeRAM
Definition: AudioPlayer.h:86
AudioPlayer::getVolume
uint8_t getVolume()
AudioPlayer::setVolume
void setVolume(uint8_t vol)
AudioPlayer::isWebStream
bool isWebStream()
Definition: AudioPlayer.h:162
AudioBuffer::m_resBuffSize
size_t m_resBuffSize
Definition: AudioPlayer.h:91
AudioBuffer::m_readPtr
uint8_t * m_readPtr
Definition: AudioPlayer.h:94
AudioPlayer::setPinout
bool setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN=I2S_PIN_NO_CHANGE)
AudioPlayer::stopSong
void stopSong()
AudioBuffer::m_freeSpace
size_t m_freeSpace
Definition: AudioPlayer.h:88
AudioPlayer::connecttospeech
bool connecttospeech(const char *speech, const char *lang)
AudioBuffer::bytesWasRead
void bytesWasRead(size_t br)
AudioPlayer::setTone
void setTone(uint8_t l_type=0, uint16_t l_freq=0, uint8_t r_type=0, uint16_t r_freq=0)
AudioBuffer::getWritePos
uint32_t getWritePos()
AudioBuffer
Definition: AudioPlayer.h:42
AudioBuffer::m_writeSpace
size_t m_writeSpace
Definition: AudioPlayer.h:89
AudioBuffer::getReadPos
uint32_t getReadPos()
AudioBuffer::m_endPtr
uint8_t * m_endPtr
Definition: AudioPlayer.h:95
AudioPlayer::getBitsPerSample
uint8_t getBitsPerSample()
AudioBuffer::AudioBuffer
AudioBuffer()
AudioBuffer::m_writePtr
uint8_t * m_writePtr
Definition: AudioPlayer.h:93
AudioPlayer::setBalance
void setBalance(int8_t bal=0)
AudioPlayer::forceMono
void forceMono(bool m)
AudioBuffer::resetBuffer
void resetBuffer()
AudioBuffer::init
size_t init(size_t bufSize=0)
AudioPlayer::isLocalFile
bool isLocalFile()
Definition: AudioPlayer.h:161