// file: $isip/class/mmedia/AudioFile/adf_08.cc // version: $Id: adf_08.cc 9286 2003-08-05 19:46:21Z parihar $ // // isip include files // #include "AudioFile.h" // method: getData // // arguments: none // Vector& data: (output) the audio data // int32 start_samp: (input) the start sample of the audio data // int32 num_samp: (input) the number of samples // // return: number of samples read // // this method gets the filtered data, if they are already in the circular // buffer, get them directly, or else get them from the audio file and filter // them // int32 AudioFile::getData(Vector& data_a, int32 start_samp_a, int32 num_samp_a) { // loop over each channel // int32 num_read = 0; // file is binary format // for (int32 ctag = 0; ctag < num_channels_d; ctag++) { int32 n = getData(data_a(ctag), ctag, start_samp_a, num_samp_a); // error check // if (n < 0) { return -1; } num_read += n; } // exit gracefully // return num_read; } // method: getData // // arguments: none // VectorFloat& data: (output) the audio data // int32 ctag: (input) the channel to read // int32 start_samp: (input) the start sample of the audio data // int32 num_samp: (input) the duration of the audio data // // return: number of samples read // // this method gets the filtered data, if they are already in the circular // buffer, get them directly, or else get them from the audio file and filter // them // int32 AudioFile::getData(VectorFloat& data_a, int32 ctag_a, int32 start_samp_a, int32 num_samp_a) { // check argument // if (ctag_a < 0) { Error::handle(name(), L"getData", Error::ARG, __FILE__, __LINE__); return -1; } // bad arguments // if (num_samp_a < 0) { Error::handle(name(), L"getData", Error::ARG, __FILE__, __LINE__); return -1; } // this is a common loop termination condition // else if (num_samp_a == 0) { data_a.clear(); return -1; } int32 output_index = 0; // determine the maximum sample in the file // int32 total_nsamp = getNumSamples(); // possibly clip the number of samples based on file length // if ((start_samp_a + num_samp_a) > total_nsamp) { num_samp_a = total_nsamp - start_samp_a; if (debug_level_d >= Integral::DETAILED) { String output; output.assign(num_samp_a); output.insert(L"getData: clipping nsamp to ", 0); Console::put(output); } } if (num_samp_a <= 0) { data_a.setLength(0); return 0; } // calculate the total number of bytes to read // data_a.setLength(num_samp_a); // get the start and end time of the data already in circular buffer // int32 buf_duration = buffers_d(ctag_a).getNumElements(); int32 buf_end = buf_end_samp_d(ctag_a); int32 buf_start = getStartSamp(ctag_a); int32 buf_max_sample = buf_end + (((int32)buf_size_d - 1) * (block_size_d / (sample_num_bytes_d * num_channels_d))); // do we need samples that start before the circular buffer starts? // // or is the start sample much farther in the future, such that the // current samples would be pushed out before we get there? // // if so we need to reset everything and start over. // if ((start_samp_a < buf_start) || (start_samp_a > buf_max_sample)) { if (debug_level_d >= Integral::BRIEF) { String output(L"clearing bufs..."); Console::put(output); } // clear out buffer // resetBuffer(ctag_a); // determine the new starting point that lines up on a block boundary // buf_start = blockFloor(start_samp_a); buf_duration = 0; // set pointers and time indices // buf_end = buf_start - 1; for (int32 x = 0; x < buf_end_samp_d.length(); x++) { buf_end_samp_d(x) = buf_end; } } // do we need to read ? // while (!end_of_file_d && ((start_samp_a + num_samp_a - 1) > getEndSamp(ctag_a))) { if (debug_level_d >= Integral::DETAILED) { String output; String numeric; output.assign(L"we need to read since "); numeric.assign(start_samp_a); output.concat(numeric); output.concat(L"+"); numeric.assign(num_samp_a); output.concat(numeric); output.concat(L"="); numeric.assign(start_samp_a + num_samp_a); output.concat(numeric); output.concat(L" > "); numeric.assign(getEndSamp(ctag_a)); output.concat(numeric); Console::put(output); } // pull in the next block of data // appendData(); if (debug_level_d >= Integral::ALL) { String output; output.assign(getEndSamp(ctag_a)); output.insert(L"now end_samp = ", 0); Console::put(output); } // recalculate boundaries // buf_duration = buffers_d(ctag_a).getNumElements(); buf_start = getStartSamp(ctag_a); // copy over the part of the user's vector we can // if ((start_samp_a + output_index) >= buf_start) { // we now give the user either: // the number of bytes they asked for, or // the all the bytes in the buffer if not all are available // // last_samp itself will not be read in // Long last_samp; last_samp.min((int32)buf_end_samp_d(ctag_a) + 1, start_samp_a + num_samp_a); // the absolute reference we keep as the buf_end_samp_d is the // last valid element, represented by the zeroeth element in the // buffer. everything valid is non-positive. // int32 buf_index = start_samp_a + output_index - buf_end_samp_d(ctag_a); while ((output_index + start_samp_a) < last_samp) { data_a(output_index++) = buffers_d(ctag_a)(buf_index++); } } } // possibly pull data from the circular buffer // if (output_index < num_samp_a) { // make sure the data is in the buffer // buf_duration = buffers_d(ctag_a).getNumElements(); buf_start = getStartSamp(ctag_a); if ((start_samp_a + output_index) < buf_start) { Error::handle(name(), L"getData", ERR, __FILE__, __LINE__); return -1; } if ((start_samp_a + num_samp_a - 1) > buf_end_samp_d(ctag_a)) { buf_end_samp_d.debug(L"buf_end_samp"); Error::handle(name(), L"getData", ERR, __FILE__, __LINE__); return -1; } // copy over data from the circular buffer to the output vector // int32 buf_index = start_samp_a + output_index - buf_end_samp_d(ctag_a); while ((output_index) < (num_samp_a)) { data_a(output_index++) = buffers_d(ctag_a)(buf_index++); } } // exit gracefully // return num_samp_a; } // method: blockFloor // // arguments: // int32 samp_num: (input) exact sample number // // return: a rounded sample number (floored by block boundaries) // // this method gets the begining sample number of the block this // sample belongs to // int32 AudioFile::blockFloor(int32 samp_num_a) const { int32 samples_per_block = block_size_d / sample_num_bytes_d; int32 i = 0; for (; i < samp_num_a; i += samples_per_block); if (i > 0) { i -= samples_per_block; } return i; } // method: blockCeil // // arguments: // int32 samp_num: (input) exact sample number // // return: a rounded sample number // // this method gets the starting sample number of the next block // int32 AudioFile::blockCeil(int32 samp_num_a) const { int32 samples_per_block = block_size_d / sample_num_bytes_d; int32 i = 0; for (; i < samp_num_a; i += samples_per_block); if (i == 0) { i += samples_per_block; } return i; } // method: appendData // // arguments: none // // return: a bool8 value indicating status // // this method reads the next block of data from the audiofile and places // it on the circular buffer // bool8 AudioFile::appendData() { if (end_of_file_d) { return false; } // determine the current location of the buffer // int32 first_samp = (int32)buf_end_samp_d(0) + 1; int32 block_samp = block_size_d / (sample_num_bytes_d * num_channels_d); for (int32 ctag = 0; ctag < num_channels_d; ctag++) { // determine if we are about to overwrite data // int32 num_bytes_left = (buffers_d(ctag).getCapacity() - buffers_d(ctag).getNumElements()) * sample_num_bytes_d; if (num_bytes_left < (block_size_d / num_channels_d)) { if (debug_level_d >= Integral::DETAILED) { String output(L"Channel "); output.concat(ctag); output.concat(L": num_bytes_left = "); output.concat(num_bytes_left); output.concat(L"; clearing out old block"); Console::put(output); } // clear out the oldest block by advancing the read pointer // buffers_d(ctag).setRead(block_samp); } } // read a block of data // Vector raw_data; // compute time boundaries for the next block // // channel is ignored, it gives you all of them // int32 nsamp = readAudioData(raw_data, -1, first_samp, block_samp); if (debug_level_d >= Integral::DETAILED) { String numeric; String output(L"read "); numeric.assign(nsamp); output.concat(numeric); output.concat(L" samples, ending at file position "); numeric.assign(tell()); output.concat(numeric); Console::put(output); } if (nsamp != block_samp) { if (debug_level_d >= Integral::DETAILED) { String numeric; String output(L"end of file at sample "); numeric.assign(nsamp + first_samp); output.concat(numeric); Console::put(output); } end_of_file_d = true; } for (int32 ctag = 0; ctag < num_channels_d; ctag++) { for (int32 i = 0; i < nsamp; i++) { // insert the input data into ma filter memory // ma_mem_d(ctag).append(raw_data(ctag)(i)); ma_mem_d(ctag).setRead(1); if (ma_mem_d(ctag).getNumElements() > 1) { ma_mem_d(ctag).seekCurr(1); } Float sum = 0.0; // handle ma coefficients // for (int32 i = 0; i < ma_coeff_d.length(); i++) { sum += ma_coeff_d(i) * ma_mem_d(ctag)(-i); } // handle ar coefficients with non-zero lag (note that read has // not yet been updated yet, so ar[0] is corresponds to the same // time as ma[-1]. // for (int32 i = 0; i < (ar_coeff_d.length() - 1); i++) { sum += ar_coeff_d(i + 1) * ar_mem_d(ctag)(-i); } // handle the ar[0] term as output gain, note: // // y[n] = x[n] + 0.9 * y[n]; // // (1 - 0.9) * y[n] = x[n]; // // y[n] = x[n] / (1 - 0.9) // // therefore, sum = sum / (1 - ar[0]) // // update: for now we enforce that ar[0] must be 1, still waiting // on mathematics to clear it up. // // sum /= (1 - ar_coeff_d(0)); // insert the output into both the data circular buffer and the ar // memory. // ar_mem_d(ctag).append(sum); ar_mem_d(ctag).setRead(1); if (ar_mem_d(ctag).getNumElements() > 1) { ar_mem_d(ctag).seekCurr(1); } buffers_d(ctag).append(sum); // only seek if we can // if (buffers_d(ctag).getNumElements() > 1) { buffers_d(ctag).seekCurr(1); } buf_end_samp_d(ctag) += 1; } } // exit gracefully // return true; } // method: getNumSamples // // arguments: none // // return: the int32 value of the number of samples // // this method gets the number of samples // int32 AudioFile::getNumSamples() const { int32 total_nsamp = -1; // format: RAW // if (file_format_d == RAW) { String output; String values; int32 lines = 0; // save the current file position and seek end // int32 cur_file_pos = tell(); if (file_type_d == TEXT) { const_cast(this)->rewind(); while (!eof()) { lines++; values.assign(lines); values.concat(L": "); const_cast(this)->get(output); values.concat(output); // Console::put(values); } const_cast(this)->rewind(); total_nsamp = lines / num_channels_d; const_cast(this)->seek(cur_file_pos, POS); } else { // seek end // const_cast(this)->seek(0, POS_PLUS_END); int32 total_len = tell(); // compute the total number of samples // int32 bytes_per_samp = (sample_num_bytes_d * num_channels_d); total_nsamp = total_len / bytes_per_samp; // resume the file position // const_cast(this)->seek(cur_file_pos, POS); } } // format: SOF // else if (file_format_d == SOF) { if (sof_d == (Sof*)NULL) { return Error::handle(name(), L"getNumSamples", Error::MEM, __FILE__, __LINE__); } if (sof_d->isBinary()) { sof_d->seek(sof_length_pos_d, File::POS); Long len; len.readData(*sof_d, PARAM_DATA); total_nsamp = (int32)len / (int32)num_channels_d; } else { int32 len = sof_d->getVecParser().countTokens(SofParser::implicitPname()); total_nsamp = len / (int32)num_channels_d; } } #ifdef HAVE_SPHERE // format: SPHERE // else if (file_format_d == SPHERE) { if (sp_file_d == (SP_FILE*)NULL) { return Error::handle(name(), L"getNumSamples", Error::MEM, __FILE__, __LINE__); } if (sp_h_get_field(sp_file_d, "sample_count", T_INTEGER, (void**)&total_nsamp)> (int32)0) { return Error::handle(name(), L"getNumSamples", Error::READ, __FILE__, __LINE__); } } #endif #ifdef HAVE_AUDIOFILE // format: supported by SGI's audiofile library // else { if (af_file_d == (AFfilehandle)NULL) { return Error::handle(name(), L"getNumSamples", Error::MEM, __FILE__, __LINE__); } total_nsamp = afGetFrameCount(af_file_d, AF_DEFAULT_TRACK); } #endif // return the number of samples // return total_nsamp; } // method: readAudioData // // arguments: // Vector& data: (output) the audio data // int32 ctag: (input) the channel to read // int32 start_samp: (input) the start time of the audio data // int32 num_samp: (input) the end time of the audio data // // return: number of samples read // // this method gets data from the audio file and put each channel data into a // VectorFloat // int32 AudioFile::readAudioData(Vector& data_a, int32 ctag_a, int32 start_samp_a, int32 num_samp_a) { // make sure the file is open // if (!isOpen()) { return Error::handle(name(), L"", ERR_NOTOPN, __FILE__, __LINE__); } // for binary read // if (file_format_d == RAW) { return readRawData(data_a, ctag_a, start_samp_a, num_samp_a); } // sof file // else if (file_format_d == SOF) { return readSofData(data_a, ctag_a, start_samp_a, num_samp_a); } // for sphere file // else if (file_format_d == SPHERE) { return readSphereData(data_a, ctag_a, start_samp_a, num_samp_a); } // file types supported by sgi's audiofile library // else { return readAFData(data_a, ctag_a, start_samp_a, num_samp_a); } // exit greacefully // return true; }