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