Bridges-C++  3.2.0
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[4]; // RIFF string
18  unsigned int overall_size ; // overall size of file in bytes
19  unsigned char wave[4]; // WAVE string
20  unsigned char fmt_chunk_marker[4]; // 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[4]; // 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  };
34 
35  namespace datastructure {
101  class AudioClip : public DataStructure {
102  const bool debug = false;
103  private:
104  int sampleCount;
105  int numChannels;
106  int sampleRate;
107  int sampleBits;
108  vector<AudioChannel> channels;
109 
110  union ShortByteUnion {
111  signed short asShort;
112  unsigned char asBytes[2];
113  };
114 
115  public:
123  AudioClip(int sampleCount, int numChannels, int sampleBits, int sampleRate) {
124  if (sampleCount > 1000000000) {
125  throw "sampleCount must be less than 1 million";
126  }
127 
128 
129  if (sampleBits != 8 && sampleBits != 16 && sampleBits != 24 && sampleBits != 32) {
130  throw "sampleBits must be either 8, 16, 24, or 32";
131  }
132 
133  if (numChannels <= 0) {
134  throw "numChannels should be positive";
135  }
136 
137  if (sampleRate <= 0) {
138  throw "sampleRate should be positive";
139  }
140 
141 
142 
143  this->sampleCount = sampleCount;
144  this->numChannels = numChannels;
145  this->channels = vector<AudioChannel>();
146 
147  for (int i = 0; i < numChannels; i++) {
148  this->channels.push_back( AudioChannel(sampleCount));
149 
150  for (int j = 0; j < sampleCount; j++) {
151  this->channels[i].setSample(j, 0);
152  }
153  }
154 
155  this->sampleRate = sampleRate;
156  this->sampleBits = sampleBits;
157  }
158 
159  virtual const string getDStype() const override {
160  return "Audio";
161  }
162 
169  AudioClip(const string& wave_file) {
170  parseWaveFile (wave_file);
171 
172  }
173 
174  virtual const string getDataStructureRepresentation() const override final {
176 
177  int len = numChannels * sampleCount * (sampleBits / 8);
178  std::vector<BYTE> byteBuff;
179 
180  int checkSampleBits = sampleBits;
181 
182  for (int i = 0; i < sampleCount; i++) {
183  for (int c = 0; c < numChannels; c++) {
184  int num = getSample(c, i);
185  //uint32_t s = ((num>>24)&0xff) | ((num<<8)&0xff0000) | ((num>>8)&0xff00) | ((num<<24)&0xff000000);
186  if (this->sampleBits == 8) {
187  byteBuff.push_back(num & 0x000000FF);
188  }
189  else if (this->sampleBits == 16) {
190  ShortByteUnion sbu;
191  sbu.asShort = (uint16_t)num;
192  byteBuff.push_back(sbu.asBytes[0]);
193  byteBuff.push_back(sbu.asBytes[1]);
194  }
195  else if (this->sampleBits == 32 || this->sampleBits == 24) {
196  // Some browsers cannot play 32 bit audio files so use 16
197  int minmax = (int)((pow(2, sampleBits) / 2) - 1);
198  int minmax16 = (int)((pow(2, 16) / 2) - 1);
199 
200  num = (int)((num / (float)minmax) * minmax16) & 0xFFFF;
201 
202  ShortByteUnion sbu;
203  sbu.asShort = (uint16_t)num;
204  byteBuff.push_back(sbu.asBytes[0]);
205  byteBuff.push_back(sbu.asBytes[1]);
206  checkSampleBits = 16;
207  }
208  }
209  }
210 
211  string b64str = base64::encode(&(byteBuff[0]), byteBuff.size());
212 
213  string jsonString = QUOTE + "encoding" + QUOTE + COLON + QUOTE + "RAW" + QUOTE + COMMA +
214  QUOTE + "numChannels" + QUOTE + COLON + JSONencode(this->numChannels) + COMMA +
215  QUOTE + "sampleRate" + QUOTE + COLON + JSONencode(this->sampleRate) + COMMA +
216  QUOTE + "bitsPerSample" + QUOTE + COLON + JSONencode(checkSampleBits) + COMMA +
217  QUOTE + "numSamples" + QUOTE + COLON + JSONencode(this->sampleCount) + COMMA +
218  QUOTE + "samples" + QUOTE + COLON + QUOTE + b64str + QUOTE + CLOSE_CURLY;
219 
220  return jsonString;
221  }
222 
227  int getNumChannels() const {
228  return this->numChannels;
229  }
235  int getSampleRate() const {
236  return this->sampleRate;
237  }
238 
246  int getSampleCount() const {
247  return this->sampleCount;
248  }
249 
250 
264  int getSampleBits() const {
265  return this->sampleBits;
266  }
267 
277  int getSample(int channelIndex, int sampleIndex) const {
278  return channels.at(channelIndex).getSample(sampleIndex);
279  }
288  void setSample(int channelIndex, int sampleIndex, int value) {
289  if (value >= pow(2, getSampleBits() - 1) ||
290  value < -pow(2, getSampleBits() - 1))
291  throw "Audio value Out of Bound";
292 
293  channels[channelIndex].setSample(sampleIndex, value);
294  }
295  private:
304  void parseWaveFile (const string & wave_file) {
305  // Read and parse an audio file in WAVE format
306 
307  // open file
308  ifstream infile;
309  infile.open (wave_file.c_str(), ios::binary | ios::in);
310  if (infile.fail()) {
311  throw "Could not open " + wave_file;
312  }
313 
314  // read the header data of the input WAVE file
315  WaveHeader wave_header = readWaveHeader(infile);
316 
317  long size_of_each_sample = (wave_header.channels *
318  wave_header.bits_per_sample) / 8;
319 
320  // read the audio data
321  if (this->sampleCount > 1000000000) {
322  throw "sampleCount must be less than 1 million";
323  }
324 
325  // create storage for the audio data
326  //this->channels.resize(this->numChannels);
327  for (int i = 0; i < numChannels; i++) {
328  this->channels.push_back( AudioChannel(this->sampleCount));
329  }
330 
331  // read sample data by chunks, if PCM
332  if (wave_header.format_type == 1) { // Only PCM handled
333  long i = 0;
334  char data_buffer[size_of_each_sample];
335  int size_is_correct = true;
336 
337  // make sure that the bytes-per-sample is completely divisible
338  // by num.of channels
339  long bytes_in_each_channel = (size_of_each_sample / wave_header.channels);
340  if ((bytes_in_each_channel * wave_header.channels) != size_of_each_sample) {
341  cout << "Error: Incorrect chunk size.. " << bytes_in_each_channel
342  << ", " << wave_header.channels << ", " << size_of_each_sample << "\n";
343  size_is_correct = false;
344  }
345 
346  if (size_is_correct) {
347  // the valid amplitude range for values based on the bits per sample
348  long low_limit = 0l;
349  long high_limit = 0l;
350 
351  switch (wave_header.bits_per_sample) {
352  case 8:
353  low_limit = -128;
354  high_limit = 127;
355  break;
356  case 16:
357  low_limit = -32768;
358  high_limit = 32767;
359  break;
360  case 24:
361  low_limit = -16777216;
362  high_limit = 16777215;
363  break;
364  case 32:
365  low_limit = -2147483648;
366  high_limit = 2147483647;
367  break;
368  }
369  for (int sample = 0; sample < this->sampleCount;
370  sample++) {
371  int amplitude;
372  if (!infile.fail()) {
373  for (int ch = 0; ch < wave_header.channels;
374  ch++ ) {
375  // read signal amplitude
376  infile.read(data_buffer, bytes_in_each_channel);
377  // convert data from big endian to little
378  // endian based on bytes in each channel sample
379  switch (bytes_in_each_channel) {
380  case 1:
381  amplitude = data_buffer[0] & 0x00ff;
382  amplitude -= 128; //in wave, 8-bit are unsigned, so shifting to signed
383  break;
384  case 2:
385  amplitude =
386  (data_buffer[0] & 0x00ff) |
387  (data_buffer[1] << 8);
388  break;
389  case 3:
390  amplitude =
391  (data_buffer[0] & 0x00ff) |
392  ((data_buffer[1] & 0x00ff) << 8) |
393  (data_buffer[2] << 16);
394  break;
395  case 4:
396  amplitude =
397  (data_buffer[0] & 0x00ff) |
398  ((data_buffer[1] & 0x00ff) << 8) |
399  ((data_buffer[2] & 0x00ff) << 16) |
400  (data_buffer[3] << 24);
401  break;
402  }
403  this->setSample(ch, sample, amplitude);
404  }
405  }
406  else {
407  cout << "Error reading file\n.";
408  break;
409  }
410  }
411  }
412  }
413  infile.close();
414  }
415 
416  //For information on the wave header, see: http://soundfile.sapp.org/doc/WaveFormat/
417  //But be careful that this URL ignores the possibility that other chunks may exist such as the fact, ce, playlist, and data list chunks. see https://en.wikipedia.org/wiki/WAV and https://en.wikipedia.org/wiki/Resource_Interchange_File_Format for details.
418  //It appears this is a complete specification: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf
419  // this function reads/parse the RIFF/WAVE formated file and stops reading at the beginning of the data chunk after reading its header
420  WaveHeader readWaveHeader(ifstream& infile) {
421 
422  // read file header
423  WaveHeader wave_header;
424 
425  infile.read ((char *)wave_header.riff, 4);
426 
427  if (wave_header.riff[0] != 'R' ||
428  wave_header.riff[1] != 'I' ||
429  wave_header.riff[2] != 'F' ||
430  wave_header.riff[3] != 'F')
431  throw "malformed RIFF header";
432 
433 
434  unsigned char *buffer = new unsigned char[4];
435  infile.read ((char*) buffer, 4);
436 
437  // convert little endian to big endian 4 byte int
438  wave_header.overall_size = buffer[0] | (buffer[1] << 8) |
439  (buffer[2] << 16) | (buffer[3] << 24);
440 
441  if (debug)
442  std::cout << "overall size: " << wave_header.overall_size << std::endl;
443 
444  infile.read ((char*) wave_header.wave, 4);
445 
446  if (wave_header.wave[0] != 'W' ||
447  wave_header.wave[1] != 'A' ||
448  wave_header.wave[2] != 'V' ||
449  wave_header.wave[3] != 'E')
450  throw "format is not WAVE";
451 
452 
453  infile.read ((char*) wave_header.fmt_chunk_marker, 4);
454  if (wave_header.fmt_chunk_marker[0] != 'f' ||
455  wave_header.fmt_chunk_marker[1] != 'm' ||
456  wave_header.fmt_chunk_marker[2] != 't' ||
457  wave_header.fmt_chunk_marker[3] != ' ')
458  throw "malformed wave file";
459 
460 
461  infile.read ((char *) buffer, 4);
462  wave_header.length_of_fmt = buffer[0] | (buffer[1] << 8) |
463  (buffer[2] << 16) | (buffer[3] << 24);
464 
465  if (debug)
466  std::cout << "length of format: " << wave_header.length_of_fmt << std::endl;
467 
468  char *buffer2 = new char[2];
469  infile.read (buffer2, 2);
470  wave_header.format_type = buffer2[0] | (buffer2[1] << 8);
471 
472  string format_name = "";
473  switch (wave_header.format_type) {
474  case 1 :
475  format_name = "PCM";
476  break;
477  case 6 :
478  format_name = "A-law";
479  break;
480  case 7 :
481  format_name = "Mu-law";
482  break;
483  default:
484  throw "unsupported format";
485 
486  }
487 
488  infile.read (buffer2, 2);
489  wave_header.channels = buffer2[0] | (buffer2[1] << 8);
490  this->numChannels = wave_header.channels;
491 
492  if (debug)
493  std::cout << "numChannels: " << numChannels << std::endl;
494 
495 
496  infile.read ((char *) buffer, 4);
497  wave_header.sample_rate = buffer[0] | (buffer[1] << 8) |
498  (buffer[2] << 16) | (buffer[3] << 24);
499  this->sampleRate = wave_header.sample_rate;
500 
501  if (debug)
502  std::cout << "sampleRate: " << sampleRate << std::endl;
503 
504  infile.read ((char *) buffer, 4);
505  wave_header.byterate = buffer[0] | (buffer[1] << 8) |
506  (buffer[2] << 16) | (buffer[3] << 24);
507 
508  if (debug)
509  std::cout << "byte rate: " << wave_header.byterate << std::endl;
510 
511  infile.read (buffer2, 2);
512  wave_header.block_align = buffer2[0] | (buffer2[1] << 8);
513 
514  infile.read (buffer2, 2);
515  wave_header.bits_per_sample = buffer2[0] | (buffer2[1] << 8);
516 
517  this->sampleBits = wave_header.bits_per_sample;
518 
519  if (debug)
520  std::cout << "sample Bits: " << sampleBits << std::endl;
521 
522  if (wave_header.byterate !=
523  wave_header.sample_rate * wave_header.channels * wave_header.bits_per_sample / 8)
524  throw "malformed wave file";
525 
526  //The next sub-chunk of the RIFF format should be the data subchunk.
527  //but in some case, there are meta data found first.
528  //skipping any sub-chunk that is not the data sub-chunk
529  bool data_chunk_found = false;
530  while (!data_chunk_found) {
531  infile.read ((char *)wave_header.data_chunk_header, 4);
532 
533  infile.read ((char *) buffer, 4);
534  wave_header.data_size = buffer[0] | (buffer[1] << 8) |
535  (buffer[2] << 16) | (buffer[3] << 24);
536 
537  if (wave_header.data_chunk_header[0] != 'd' ||
538  wave_header.data_chunk_header[1] != 'a' ||
539  wave_header.data_chunk_header[2] != 't' ||
540  wave_header.data_chunk_header[3] != 'a') {
541  //skip sub chunk
542  int padding = (wave_header.data_size % 2 ? 0 : 1);
543  infile.ignore(wave_header.data_size + padding);
544  }
545  else
546  data_chunk_found = true;
547  }
548 
549 
550 
551  // calculate no.of samples
552  long num_samples = (8 * wave_header.data_size) /
553  (wave_header.channels * wave_header.bits_per_sample);
554  this->sampleCount = num_samples;
555 
556  if (debug)
557  std::cout << "sample Count: " << this->sampleCount << std::endl;
558 
559 
560 
561  long size_of_each_sample = (wave_header.channels *
562  wave_header.bits_per_sample) / 8;
563 
564 
565 
566  delete[] buffer;
567  delete[] buffer2;
568 
569  if (!infile)
570  throw "malformed RIFF header";
571 
572  return wave_header;
573  }
574  };
575  }
576 }
virtual const string getDataStructureRepresentation() const override final
Definition: AudioClip.h:174
unsigned char data_chunk_header[4]
Definition: AudioClip.h:28
unsigned int channels
Definition: AudioClip.h:23
This is the superclass of all data structure types in BRIDGES.
Definition: DataStructure.h:73
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
class that represents an audio channel.
Definition: AudioChannel.h:9
virtual const string getDStype() const override
Definition: AudioClip.h:159
unsigned int sample_rate
Definition: AudioClip.h:24
This class provides support for reading, modifying, and playing audio waveforms.
Definition: AudioClip.h:101
const string CLOSE_CURLY
Definition: DataStructure.h:53
int getSampleCount() const
returns the number of samples in the clip
Definition: AudioClip.h:246
unsigned char riff[4]
Definition: AudioClip.h:17
AudioClip(int sampleCount, int numChannels, int sampleBits, int sampleRate)
create an audio clip
Definition: AudioClip.h:123
unsigned int length_of_fmt
Definition: AudioClip.h:21
AudioClip(const string &wave_file)
create an audio clip from a File
Definition: AudioClip.h:169
these methods convert byte arrays in to base64 codes and are used in BRIDGES to represent the color a...
Definition: alltypes.h:4
int getSampleBits() const
returns the sampling depth.
Definition: AudioClip.h:264
void setSample(int channelIndex, int sampleIndex, int value)
change a particular sample
Definition: AudioClip.h:288
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:235
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
unsigned char fmt_chunk_marker[4]
Definition: AudioClip.h:20
unsigned char wave[4]
Definition: AudioClip.h:19
int getSample(int channelIndex, int sampleIndex) const
access a particular sample
Definition: AudioClip.h:277
int getNumChannels() const
returns the number of channels of the clip
Definition: AudioClip.h:227
std::string JSONencode(const T &d)
Definition: JSONutil.h:37