arduino-audio-tools
CodecVorbis.h
1 #pragma once
2 #include "AudioConfig.h"
3 #include "AudioTools/AudioCodecs/AudioCodecsBase.h"
4 #include "vorbis-tremor.h"
5 
6 // #include "AudioTools/AudioCodecs/ContainerOgg.h"
7 // #include "ivorbiscodec.h"
8 // #include "ivorbisfile.h"
9 
10 
11 namespace audio_tools {
12 
13 #ifndef VARBIS_MAX_READ_SIZE
14 # define VARBIS_MAX_READ_SIZE 256
15 #endif
16 
17 #define VORBIS_HEADER_OPEN_LIMIT 1024
18 
28 public:
29  VorbisDecoder() = default;
30 
33  if (active) {
34  end();
35  }
36  }
37 
39  bool begin() override {
40  LOGI("begin");
41 
42  callbacks.read_func = read_func;
43  callbacks.seek_func = seek_func;
44  callbacks.close_func = close_func;
45  callbacks.tell_func = tell_func;
46 
47  if (p_input->available()>=VORBIS_HEADER_OPEN_LIMIT){
48  ovOpen();
49  }
50 
51  active = true;
52  is_first = true;
53  return true;
54  }
55 
57  void end() override {
58  LOGI("end");
59  active = false;
60  is_ov_open = false;
61  is_first = true;
62  ov_clear(&file);
63  }
64 
66  AudioInfo audioInfo() override { return cfg; }
67 
69  virtual operator bool() override { return active; }
70 
71  virtual bool copy() override {
72  // wait for data
73  if (is_first){
74  // wait for some data
75  if(p_input->available()<VORBIS_HEADER_OPEN_LIMIT){
76  delay(20);
77  return false;
78  }
79  LOGI("available: %d", p_input->available());
80  is_first = false;
81  }
82 
83  // open if not already done
84  if (!is_ov_open){
85  if (!ovOpen()) {
86  LOGE("not open");
87  return false;
88  }
89  }
90 
91  if(pcm.data()==nullptr){
92  LOGE("Not enough memory");
93  return false;
94  }
95 
96  // convert to pcm
97  long result = ov_read(&file, (char *)pcm.data(), pcm.size(), &bitstream);
98  LOGI("copy: %ld", result);
99  if (result > 0) {
100  AudioInfo current = currentInfo();
101  if (current != cfg) {
102  cfg = current;
103  cfg.logInfo();
104  notifyAudioChange(cfg);
105  }
106  p_print->write(pcm.data(), result);
107  delay(1);
108  return true;
109  } else {
110  if (result==-3){
111  // data interruption
112  LOGD("copy: %ld - %s", result, readError(result));
113  } else {
114  LOGE("copy: %ld - %s", result, readError(result));
115  }
116 
117  return false;
118  }
119  }
120 
121 protected:
122  AudioInfo cfg;
123  Vector<uint8_t> pcm;
124  OggVorbis_File file;
125  ov_callbacks callbacks;
126  bool active;
127  int bitstream;
128  bool is_first = true;
129  bool is_ov_open = false;
130 
131  bool ovOpen(){
132  pcm.resize(VARBIS_MAX_READ_SIZE);
133  int rc = ov_open_callbacks(this, &file, nullptr, 0, callbacks);
134  if (rc<0){
135  LOGE("ov_open_callbacks: %d", rc);
136  } else {
137  is_ov_open = true;
138  }
139  return is_ov_open;
140  }
141 
142  AudioInfo currentInfo() {
143  AudioInfo result;
144  vorbis_info *info = ov_info(&file, -1);
145  result.sample_rate = info->rate;
146  result.channels = info->channels;
147  result.bits_per_sample = 16;
148  return result;
149  }
150 
151  virtual size_t readBytes(uint8_t *data, size_t len) override {
152  size_t read_size = min(len,(size_t)VARBIS_MAX_READ_SIZE);
153  size_t result = p_input->readBytes((uint8_t *)data, read_size);
154  LOGD("readBytes: %zu",result);
155  return result;
156  }
157 
158  static size_t read_func(void *ptr, size_t size, size_t nmemb,
159  void *datasource) {
160  VorbisDecoder *self = (VorbisDecoder *)datasource;
161  return self->readBytes((uint8_t *)ptr, size * nmemb);
162  }
163 
164  static int seek_func(void *datasource, ogg_int64_t offset, int whence) {
165  VorbisDecoder *self = (VorbisDecoder *)datasource;
166  return -1;
167  }
168 
169  static long tell_func(void *datasource) {
170  VorbisDecoder *self = (VorbisDecoder *)datasource;
171  return -1;
172  }
173 
174  static int close_func(void *datasource) {
175  VorbisDecoder *self = (VorbisDecoder *)datasource;
176  self->end();
177  return 0;
178  }
179 
180  const char* readError(long error){
181  switch(error){
182  case OV_HOLE:
183  return "Interruption in the data";
184  case OV_EBADLINK:
185  return "Invalid stream section ";
186  case OV_EINVAL:
187  return "Invalid header";
188  default:
189  return "N/A";
190  }
191  }
192 
193 };
194 
195 } // namespace audio_tools
A Streaming Decoder where we provide both the input and output as streams.
Definition: AudioCodecsBase.h:154
Vorbis Streaming Decoder using https://github.com/pschatzmann/arduino-libvorbis-tremor.
Definition: CodecVorbis.h:27
virtual bool copy() override
Process a single read operation - to be called in the loop.
Definition: CodecVorbis.h:71
~VorbisDecoder()
Destroy the VorbisDecoder object.
Definition: CodecVorbis.h:32
void end() override
Releases the reserved memory.
Definition: CodecVorbis.h:57
bool begin() override
Starts the processing.
Definition: CodecVorbis.h:39
AudioInfo audioInfo() override
Provides the last available MP3FrameInfo.
Definition: CodecVorbis.h:66
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52