Bridges-C++  3.1.1
Bridges(C++API)
AudioClip.h
Go to the documentation of this file.
1 
2 #include "Bridges.h"
3 #include "AudioChannel.h"
4 #include <math.h>
5 #include "rapidjson/stringbuffer.h"
6 #include "base64.h"
7 #include <string>
8 #include <fstream>
9 
10 #include <iostream>
11 using namespace std;
12 
13 namespace bridges {
14 
15  // WAVE file header format
16  struct WaveHeader {
17  unsigned char *riff; // RIFF string
18  unsigned int overall_size ; // overall size of file in bytes
19  unsigned char *wave; // WAVE string
20  unsigned char *fmt_chunk_marker; // fmt string with trailing null char
21  unsigned int length_of_fmt; // length of the format data
22  unsigned int format_type; // format type. 1-PCM, 3- IEEE float, 6 - 8bit A law, 7 - 8bit mu law
23  unsigned int channels; // no.of channels
24  unsigned int sample_rate; // sampling rate (blocks per second)
25  unsigned int byterate; // SampleRate * NumChannels * BitsPerSample/8
26  unsigned int block_align; // NumChannels * BitsPerSample/8
27  unsigned int bits_per_sample; // bits per sample, 8- 8bits, 16- 16 bits etc
28  unsigned char *data_chunk_header; // DATA string or FLLR string
29  unsigned int data_size; // NumSamples * NumChannels *
30  // BitsPerSample/8 - size of the
31  // next chunk that will be read
32 
33 
35  riff = new unsigned char[4];
36  wave = new unsigned char[4];
37  fmt_chunk_marker = new unsigned char[4];
38  data_chunk_header = new unsigned char[4];
39  }
40 
42  delete[] riff;
43  delete[] wave;
44  delete[] fmt_chunk_marker;
45  delete[] data_chunk_header;
46  }
47  };
48 
49  namespace datastructure {
115  class AudioClip : public DataStructure {
116  private:
117  int sampleCount;
118  int numChannels;
119  int sampleRate;
120  int sampleBits;
121  vector<AudioChannel *> channels;
122 
123  union ShortByteUnion {
124  signed short asShort;
125  unsigned char asBytes[2];
126  };
127 
128  public:
136  AudioClip(int sampleCount, int numChannels, int sampleBits, int sampleRate) {
137  if (sampleCount > 1000000000) {
138  throw "sampleCount must be less than 1 million";
139  }
140 
141 
142  if (sampleBits != 8 && sampleBits != 16 && sampleBits != 24 && sampleBits != 32) {
143  throw "sampleBits must be either 8, 16, 24, or 32";
144  }
145 
146  if (numChannels <= 0) {
147  throw "numChannels should be positive";
148  }
149 
150  if (sampleRate <= 0) {
151  throw "sampleRate should be positive";
152  }
153 
154 
155 
156  this->sampleCount = sampleCount;
157  this->numChannels = numChannels;
158  this->channels = vector<AudioChannel *>(numChannels);
159 
160  for (int i = 0; i < numChannels; i++) {
161  this->channels[i] = new AudioChannel(sampleCount);
162 
163  for (int j = 0; j < sampleCount; j++) {
164  this->channels[i]->setSample(j, 0);
165  }
166  }
167 
168  this->sampleRate = sampleRate;
169  this->sampleBits = sampleBits;
170  }
171 
172  virtual const string getDStype() const override {
173  return "Audio";
174  }
175 
182  AudioClip(string wave_file) {
183  parseWaveFile (wave_file);
184 
185  }
186 
187  virtual ~AudioClip () {
188  for (int i = 0; i < numChannels; i++) {
189  delete channels[i];
190  channels[i] = nullptr;
191  }
192  }
193 
194  virtual const string getDataStructureRepresentation() const override final {
196 
197  int len = numChannels * sampleCount * (sampleBits / 8);
198  std::vector<BYTE> byteBuff;
199 
200  int checkSampleBits = sampleBits;
201 
202  for (int i = 0; i < sampleCount; i++) {
203  for (int c = 0; c < numChannels; c++) {
204  int num = getSample(c, i);
205  //uint32_t s = ((num>>24)&0xff) | ((num<<8)&0xff0000) | ((num>>8)&0xff00) | ((num<<24)&0xff000000);
206  if (this->sampleBits == 8) {
207  byteBuff.push_back(num & 0x000000FF);
208  }
209  else if (this->sampleBits == 16) {
210  ShortByteUnion sbu;
211  sbu.asShort = (uint16_t)num;
212  byteBuff.push_back(sbu.asBytes[0]);
213  byteBuff.push_back(sbu.asBytes[1]);
214  }
215  else if (this->sampleBits == 32 || this->sampleBits == 24) {
216  // Some browsers cannot play 32 bit audio files so use 16
217  int minmax = (int)((pow(2, sampleBits) / 2) - 1);
218  int minmax16 = (int)((pow(2, 16) / 2) - 1);
219 
220  num = (int)((num / (float)minmax) * minmax16) & 0xFFFF;
221 
222  ShortByteUnion sbu;
223  sbu.asShort = (uint16_t)num;
224  byteBuff.push_back(sbu.asBytes[0]);
225  byteBuff.push_back(sbu.asBytes[1]);
226  checkSampleBits = 16;
227  }
228  }
229  }
230 
231  string b64str = base64::encode(&(byteBuff[0]), byteBuff.size());
232 
233  string jsonString = QUOTE + "encoding" + QUOTE + COLON + QUOTE + "RAW" + QUOTE + COMMA +
234  QUOTE + "numChannels" + QUOTE + COLON + JSONencode(this->numChannels) + COMMA +
235  QUOTE + "sampleRate" + QUOTE + COLON + JSONencode(this->sampleRate) + COMMA +
236  QUOTE + "bitsPerSample" + QUOTE + COLON + JSONencode(checkSampleBits) + COMMA +
237  QUOTE + "numSamples" + QUOTE + COLON + JSONencode(this->sampleCount) + COMMA +
238  QUOTE + "samples" + QUOTE + COLON + QUOTE + b64str + QUOTE + CLOSE_CURLY;
239 
240  return jsonString;
241  }
242 
247  int getNumChannels() const {
248  return this->numChannels;
249  }
255  int getSampleRate() const {
256  return this->sampleRate;
257  }
258 
266  int getSampleCount() const {
267  return this->sampleCount;
268  }
269 
270 
284  int getSampleBits() const {
285  return this->sampleBits;
286  }
287 
297  int getSample(int channelIndex, int sampleIndex) const {
298  return channels.at(channelIndex)->getSample(sampleIndex);
299  }
308  void setSample(int channelIndex, int sampleIndex, int value) {
309  if (value >= pow(2, getSampleBits() - 1) ||
310  value < -pow(2, getSampleBits() - 1))
311  throw "Audio value Out of Bound";
312 
313  channels[channelIndex]->setSample(sampleIndex, value);
314  }
315  private:
324  void parseWaveFile (string wave_file) {
325  // Read and parse an audio file in WAVE format
326 
327  // open file
328  ifstream infile;
329  infile.open (wave_file.c_str(), ios::binary | ios::in);
330  if (infile.fail()) {
331  cout << "Couldnt open " << wave_file << endl;
332  return;
333  }
334 
335  // read the header data of the input WAVE file
336  WaveHeader wave_header = readWaveHeader(infile);
337 
338  long size_of_each_sample = (wave_header.channels *
339  wave_header.bits_per_sample) / 8;
340 
341  // read the audio data
342  if (this->sampleCount > 1000000000) {
343  throw "sampleCount must be less than 1 million";
344  }
345 
346  // create storage for the audio data
347  this->channels.resize(this->numChannels);
348  for (int i = 0; i < numChannels; i++) {
349  this->channels[i] = new AudioChannel(this->sampleCount);
350  }
351 
352  // read sample data by chunks, if PCM
353  if (wave_header.format_type == 1) { // Only PCM handled
354  long i = 0;
355  char data_buffer[size_of_each_sample];
356  int size_is_correct = true;
357 
358  // make sure that the bytes-per-sample is completely divisible
359  // by num.of channels
360  long bytes_in_each_channel = (size_of_each_sample / wave_header.channels);
361  if ((bytes_in_each_channel * wave_header.channels) != size_of_each_sample) {
362  cout << "Error: Incorrect chunk size.. " << bytes_in_each_channel
363  << ", " << wave_header.channels << ", " << size_of_each_sample << "\n";
364  size_is_correct = false;
365  }
366 
367  if (size_is_correct) {
368  // the valid amplitude range for values based on the bits per sample
369  long low_limit = 0l;
370  long high_limit = 0l;
371 
372  switch (wave_header.bits_per_sample) {
373  case 8:
374  low_limit = -128;
375  high_limit = 127;
376  break;
377  case 16:
378  low_limit = -32768;
379  high_limit = 32767;
380  break;
381  case 24:
382  low_limit = -16777216;
383  high_limit = 16777215;
384  break;
385  case 32:
386  low_limit = -2147483648;
387  high_limit = 2147483647;
388  break;
389  }
390  for (int sample = 0; sample < this->sampleCount;
391  sample++) {
392  int amplitude;
393  if (!infile.fail()) {
394  for (int ch = 0; ch < wave_header.channels;
395  ch++ ) {
396  // read signal amplitude
397  infile.read(data_buffer, bytes_in_each_channel);
398  // convert data from big endian to little
399  // endian based on bytes in each channel sample
400  switch (bytes_in_each_channel) {
401  case 1:
402  amplitude = data_buffer[0] & 0x00ff;
403  amplitude -= 128; //in wave, 8-bit are unsigned, so shifting to signed
404  break;
405  case 2:
406  amplitude =
407  (data_buffer[0] & 0x00ff) |
408  (data_buffer[1] << 8);
409  break;
410  case 3:
411  amplitude =
412  (data_buffer[0] & 0x00ff) |
413  ((data_buffer[1] & 0x00ff) << 8) |
414  (data_buffer[2] << 16);
415  break;
416  case 4:
417  amplitude =
418  (data_buffer[0] & 0x00ff) |
419  ((data_buffer[1] & 0x00ff) << 8) |
420  ((data_buffer[2] & 0x00ff) << 16) |
421  (data_buffer[3] << 24);
422  break;
423  }
424  this->setSample(ch, sample, amplitude);
425  }
426  }
427  else {
428  cout << "Error reading file\n.";
429  break;
430  }
431  }
432  }
433  }
434  infile.close();
435  }
436  WaveHeader readWaveHeader(ifstream& infile) {
437 
438  // read file header
439  WaveHeader wave_header;
440 
441  infile.read ((char *)wave_header.riff, 4);
442 
443  unsigned char *buffer = new unsigned char[4];
444  infile.read ((char*) buffer, 4);
445 
446  // convert little endian to big endian 4 byte int
447  wave_header.overall_size = buffer[0] | (buffer[1] << 8) |
448  (buffer[2] << 16) | (buffer[3] << 24);
449 
450 
451  infile.read ((char*) wave_header.wave, 4);
452 
453  infile.read ((char*) wave_header.fmt_chunk_marker, 4);
454  infile.read ((char *) buffer, 4);
455  wave_header.length_of_fmt = buffer[0] | (buffer[1] << 8) |
456  (buffer[2] << 16) | (buffer[3] << 24);
457 
458  char *buffer2 = new char[2];
459  infile.read (buffer2, 2);
460  wave_header.format_type = buffer2[0] | (buffer2[1] << 8);
461 
462  string format_name = "";
463  switch (wave_header.format_type) {
464  case 1 :
465  format_name = "PCM";
466  break;
467  case 6 :
468  format_name = "A-law";
469  break;
470  case 7 :
471  format_name = "Mu-law";
472  break;
473  }
474 
475  infile.read (buffer2, 2);
476  wave_header.channels = buffer2[0] | (buffer2[1] << 8);
477  this->numChannels = wave_header.channels;
478 
479  infile.read ((char *) buffer, 4);
480  wave_header.sample_rate = buffer[0] | (buffer[1] << 8) |
481  (buffer[2] << 16) | (buffer[3] << 24);
482  this->sampleRate = wave_header.sample_rate;
483 
484  infile.read ((char *) buffer, 4);
485  wave_header.byterate = buffer[0] | (buffer[1] << 8) |
486  (buffer[2] << 16) | (buffer[3] << 24);
487 
488  infile.read (buffer2, 2);
489  wave_header.block_align = buffer2[0] | (buffer2[1] << 8);
490 
491  infile.read (buffer2, 2);
492  wave_header.bits_per_sample = buffer2[0] | (buffer2[1] << 8);
493 
494  this->sampleBits = wave_header.bits_per_sample;
495 
496  infile.read ((char *)wave_header.data_chunk_header, 4);
497 
498  infile.read ((char *) buffer, 4);
499  wave_header.data_size = buffer[0] | (buffer[1] << 8) |
500  (buffer[2] << 16) | (buffer[3] << 24);
501 
502  // calculate no.of samples
503  long num_samples = (8 * wave_header.data_size) /
504  (wave_header.channels * wave_header.bits_per_sample);
505  this->sampleCount = num_samples;
506 
507  long size_of_each_sample = (wave_header.channels *
508  wave_header.bits_per_sample) / 8;
509 
510  delete[] buffer;
511  delete[] buffer2;
512 
513 
514  return wave_header;
515  }
516  };
517  }
518 }
WaveHeader()
Definition: AudioClip.h:34
virtual const string getDataStructureRepresentation() const override final
Definition: AudioClip.h:194
unsigned int channels
Definition: AudioClip.h:23
This is the superclass of all data structure types in BRIDGES.
Definition: DataStructure.h:73
unsigned char * riff
Definition: AudioClip.h:17
~WaveHeader()
Definition: AudioClip.h:41
string encode(BYTE const *buf, unsigned int bufLen)
Definition: base64.h:59
const string COLON
Definition: DataStructure.h:51
STL namespace.
unsigned int format_type
Definition: AudioClip.h:22
unsigned char * fmt_chunk_marker
Definition: AudioClip.h:20
Definition: AudioChannel.h:4
virtual const string getDStype() const override
Definition: AudioClip.h:172
unsigned int sample_rate
Definition: AudioClip.h:24
virtual ~AudioClip()
Definition: AudioClip.h:187
This class provides support for reading, modifying, and playing audio waveforms.
Definition: AudioClip.h:115
const string CLOSE_CURLY
Definition: DataStructure.h:53
int getSampleCount() const
returns the number of samples in the clip
Definition: AudioClip.h:266
AudioClip(int sampleCount, int numChannels, int sampleBits, int sampleRate)
create an audio clip
Definition: AudioClip.h:136
unsigned int length_of_fmt
Definition: AudioClip.h:21
these methods convert byte arrays in to base64 codes and are used in BRIDGES to represent the color a...
Definition: alltypes.h:4
unsigned char * data_chunk_header
Definition: AudioClip.h:28
int getSampleBits() const
returns the sampling depth.
Definition: AudioClip.h:284
void setSample(int channelIndex, int sampleIndex, int value)
change a particular sample
Definition: AudioClip.h:308
unsigned char * wave
Definition: AudioClip.h:19
unsigned int block_align
Definition: AudioClip.h:26
unsigned int byterate
Definition: AudioClip.h:25
int getSampleRate() const
returns the sampling rate of the clip
Definition: AudioClip.h:255
unsigned int data_size
Definition: AudioClip.h:29
unsigned int bits_per_sample
Definition: AudioClip.h:27
Definition: AudioClip.h:16
unsigned int overall_size
Definition: AudioClip.h:18
const string COMMA
Definition: DataStructure.h:50
const string QUOTE
Definition: DataStructure.h:49
AudioClip(string wave_file)
create an audio clip from a File
Definition: AudioClip.h:182
int getSample(int channelIndex, int sampleIndex) const
access a particular sample
Definition: AudioClip.h:297
int getNumChannels() const
returns the number of channels of the clip
Definition: AudioClip.h:247
std::string JSONencode(const T &d)
Definition: JSONutil.h:37