Bridges-C++ 3.5.0-dev1
Bridges(C++ API)
Loading...
Searching...
No Matches
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>
11using namespace std;
12
13namespace 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
264 void setSampleBits(int bit_depth) {
265 if (bit_depth == 8 || bit_depth == 16 || bit_depth == 24 || bit_depth == 32)
266 this->sampleBits = bit_depth;
267 else
268 cerr << "Bit rate must 8,16,24 or 32!" << "\n";
269 }
270
280 int getSample(int channelIndex, int sampleIndex) const {
281 return channels.at(channelIndex).getSample(sampleIndex);
282 }
291 void setSample(int channelIndex, int sampleIndex, int value) {
292 if (value >= pow(2, getSampleBits() - 1) ||
293 value < -pow(2, getSampleBits() - 1))
294 throw "Audio value Out of Bound";
295
296 channels[channelIndex].setSample(sampleIndex, value);
297 }
298 private:
307 void parseWaveFile (const string & wave_file) {
308 // Read and parse an audio file in WAVE format
309
310 // open file
311 ifstream infile;
312 infile.open (wave_file.c_str(), ios::binary | ios::in);
313 if (infile.fail()) {
314 throw "Could not open " + wave_file;
315 }
316
317 // read the header data of the input WAVE file
318 WaveHeader wave_header = readWaveHeader(infile);
319
320 long size_of_each_sample = (wave_header.channels *
321 wave_header.bits_per_sample) / 8;
322
323 // read the audio data
324 if (this->sampleCount > 1000000000) {
325 throw "sampleCount must be less than 1 million";
326 }
327
328 // create storage for the audio data
329 //this->channels.resize(this->numChannels);
330 for (int i = 0; i < numChannels; i++) {
331 this->channels.push_back( AudioChannel(this->sampleCount));
332 }
333
334 // read sample data by chunks, if PCM
335 if (wave_header.format_type == 1) { // Only PCM handled
336 long i = 0;
337 char data_buffer[size_of_each_sample];
338 int size_is_correct = true;
339
340 // make sure that the bytes-per-sample is completely divisible
341 // by num.of channels
342 long bytes_in_each_channel = (size_of_each_sample / wave_header.channels);
343 if ((bytes_in_each_channel * wave_header.channels) != size_of_each_sample) {
344 cout << "Error: Incorrect chunk size.. " << bytes_in_each_channel
345 << ", " << wave_header.channels << ", " << size_of_each_sample << "\n";
346 size_is_correct = false;
347 }
348
349 if (size_is_correct) {
350 // the valid amplitude range for values based on the bits per sample
351 long low_limit = 0l;
352 long high_limit = 0l;
353
354 switch (wave_header.bits_per_sample) {
355 case 8:
356 low_limit = -128;
357 high_limit = 127;
358 break;
359 case 16:
360 low_limit = -32768;
361 high_limit = 32767;
362 break;
363 case 24:
364 low_limit = -16777216;
365 high_limit = 16777215;
366 break;
367 case 32:
368 low_limit = -2147483648;
369 high_limit = 2147483647;
370 break;
371 }
372 for (int sample = 0; sample < this->sampleCount;
373 sample++) {
374 int amplitude;
375 if (!infile.fail()) {
376 for (int ch = 0; ch < wave_header.channels;
377 ch++ ) {
378 // read signal amplitude
379 infile.read(data_buffer, bytes_in_each_channel);
380 // convert data from big endian to little
381 // endian based on bytes in each channel sample
382 switch (bytes_in_each_channel) {
383 case 1:
384 amplitude = data_buffer[0] & 0x00ff;
385 amplitude -= 128; //in wave, 8-bit are unsigned, so shifting to signed
386 break;
387 case 2:
388 amplitude =
389 (data_buffer[0] & 0x00ff) |
390 (data_buffer[1] << 8);
391 break;
392 case 3:
393 amplitude =
394 (data_buffer[0] & 0x00ff) |
395 ((data_buffer[1] & 0x00ff) << 8) |
396 (data_buffer[2] << 16);
397 break;
398 case 4:
399 amplitude =
400 (data_buffer[0] & 0x00ff) |
401 ((data_buffer[1] & 0x00ff) << 8) |
402 ((data_buffer[2] & 0x00ff) << 16) |
403 (data_buffer[3] << 24);
404 break;
405 }
406 this->setSample(ch, sample, amplitude);
407 }
408 }
409 else {
410 cout << "Error reading file\n.";
411 break;
412 }
413 }
414 }
415 }
416 infile.close();
417 }
418
419 //For information on the wave header, see: http://soundfile.sapp.org/doc/WaveFormat/
420 //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.
421 //It appears this is a complete specification: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf
422 // this function reads/parse the RIFF/WAVE formated file and stops reading at the beginning of the data chunk after reading its header
423 WaveHeader readWaveHeader(ifstream& infile) {
424
425 // read file header
426 WaveHeader wave_header;
427
428 infile.read ((char *)wave_header.riff, 4);
429
430 if (wave_header.riff[0] != 'R' ||
431 wave_header.riff[1] != 'I' ||
432 wave_header.riff[2] != 'F' ||
433 wave_header.riff[3] != 'F')
434 throw "malformed RIFF header";
435
436 unsigned char *buffer = new unsigned char[4];
437 infile.read ((char*) buffer, 4);
438
439 // convert little endian to big endian 4 byte int
440 wave_header.overall_size = buffer[0] | (buffer[1] << 8) |
441 (buffer[2] << 16) | (buffer[3] << 24);
442
443 if (debug)
444 std::cout << "overall size: " << wave_header.overall_size << std::endl;
445
446 infile.read ((char*) wave_header.wave, 4);
447
448 if (wave_header.wave[0] != 'W' ||
449 wave_header.wave[1] != 'A' ||
450 wave_header.wave[2] != 'V' ||
451 wave_header.wave[3] != 'E')
452 throw "format is not WAVE";
453
454 infile.read ((char*) wave_header.fmt_chunk_marker, 4);
455 if (wave_header.fmt_chunk_marker[0] != 'f' ||
456 wave_header.fmt_chunk_marker[1] != 'm' ||
457 wave_header.fmt_chunk_marker[2] != 't' ||
458 wave_header.fmt_chunk_marker[3] != ' ')
459 throw "malformed wave file";
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 infile.read ((char *) buffer, 4);
496 wave_header.sample_rate = buffer[0] | (buffer[1] << 8) |
497 (buffer[2] << 16) | (buffer[3] << 24);
498 this->sampleRate = wave_header.sample_rate;
499
500 if (debug)
501 std::cout << "sampleRate: " << sampleRate << std::endl;
502
503 infile.read ((char *) buffer, 4);
504 wave_header.byterate = buffer[0] | (buffer[1] << 8) |
505 (buffer[2] << 16) | (buffer[3] << 24);
506
507 if (debug)
508 std::cout << "byte rate: " << wave_header.byterate << std::endl;
509
510 infile.read (buffer2, 2);
511 wave_header.block_align = buffer2[0] | (buffer2[1] << 8);
512
513 infile.read (buffer2, 2);
514 wave_header.bits_per_sample = buffer2[0] | (buffer2[1] << 8);
515
516 this->sampleBits = wave_header.bits_per_sample;
517
518 if (debug)
519 std::cout << "sample Bits: " << sampleBits << std::endl;
520
521 if (wave_header.byterate !=
522 wave_header.sample_rate * wave_header.channels * wave_header.bits_per_sample / 8)
523 throw "malformed wave file";
524
525 //The next sub-chunk of the RIFF format should be the data subchunk.
526 //but in some case, there are meta data found first.
527 //skipping any sub-chunk that is not the data sub-chunk
528 bool data_chunk_found = false;
529 while (!data_chunk_found) {
530 infile.read ((char *)wave_header.data_chunk_header, 4);
531
532 infile.read ((char *) buffer, 4);
533 wave_header.data_size = buffer[0] | (buffer[1] << 8) |
534 (buffer[2] << 16) | (buffer[3] << 24);
535
536 if (wave_header.data_chunk_header[0] != 'd' ||
537 wave_header.data_chunk_header[1] != 'a' ||
538 wave_header.data_chunk_header[2] != 't' ||
539 wave_header.data_chunk_header[3] != 'a') {
540 //skip sub chunk
541 int padding = (wave_header.data_size % 2 ? 0 : 1);
542 infile.ignore(wave_header.data_size + padding);
543 }
544 else
545 data_chunk_found = true;
546 }
547
548 // calculate no.of samples
549 long num_samples = (8 * wave_header.data_size) /
550 (wave_header.channels * wave_header.bits_per_sample);
551 this->sampleCount = num_samples;
552
553 if (debug)
554 std::cout << "sample Count: " << this->sampleCount << std::endl;
555
556 long size_of_each_sample = (wave_header.channels *
557 wave_header.bits_per_sample) / 8;
558
559 delete[] buffer;
560 delete[] buffer2;
561
562 if (!infile)
563 throw "malformed RIFF header";
564
565 return wave_header;
566 }
567 };
568 }
569}
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
void setSampleBits(int bit_depth)
Definition AudioClip.h:264
virtual const string getDStype() const override
Definition AudioClip.h:156
int getSample(int channelIndex, int sampleIndex) const
access a particular sample
Definition AudioClip.h:280
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:291
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:74
std::string JSONencode(const T &d)
Definition JSONutil.h:38
string encode(BYTE const *buf, unsigned int bufLen)
Definition base64.h:56
Support for drawing Bar charts.
Definition alltypes.h:4
const string COLON
Definition DataStructure.h:52
const string COMMA
Definition DataStructure.h:51
const string CLOSE_CURLY
Definition DataStructure.h:54
const string QUOTE
Definition DataStructure.h:50
Definition AudioClip.h:16
unsigned int overall_size
Definition AudioClip.h:18
unsigned char data_chunk_header[4]
Definition AudioClip.h:28
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 char fmt_chunk_marker[4]
Definition AudioClip.h:20
unsigned char wave[4]
Definition AudioClip.h:19
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
unsigned char riff[4]
Definition AudioClip.h:17