//========================================================= // MusE // Linux Music Editor // $Id: waveevent.cpp,v 1.9.2.6 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "audioconvert.h" #include "globals.h" #include "event.h" #include "waveevent.h" #include "xml.h" #include "wave.h" #include #include // Added by Tim. p3.3.18 //#define USE_SAMPLERATE //#define WAVEEVENT_DEBUG //#define WAVEEVENT_DEBUG_PRC namespace MusECore { //--------------------------------------------------------- // WaveEvent //--------------------------------------------------------- WaveEventBase::WaveEventBase(EventType t) : EventBase(t) { deleted = false; _spos = 0; } //--------------------------------------------------------- // WaveEventBase::clone //--------------------------------------------------------- EventBase* WaveEventBase::clone() const { return new WaveEventBase(*this); } bool WaveEventBase::isSimilarTo(const EventBase& other_) const { const WaveEventBase* other = dynamic_cast(&other_); if (other==NULL) // dynamic cast hsa failed: "other_" is not of type WaveEventBase. return false; return f.dirPath()==other->f.dirPath() && _spos==other->_spos && this->PosLen::operator==(*other); } //--------------------------------------------------------- // WaveEvent::mid //--------------------------------------------------------- EventBase* WaveEventBase::mid(unsigned b, unsigned e) const { WaveEventBase* ev = new WaveEventBase(*this); unsigned fr = frame(); unsigned start = fr - b; if(b > fr) { start = 0; ev->setSpos(spos() + b - fr); } unsigned end = endFrame(); if (e < end) end = e; ev->setFrame(start); ev->setLenFrame(end - b - start); return ev; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void WaveEventBase::dump(int n) const { EventBase::dump(n); } //--------------------------------------------------------- // WaveEventBase::read //--------------------------------------------------------- void WaveEventBase::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: case Xml::Attribut: return; case Xml::TagStart: if (tag == "poslen") PosLen::read(xml, "poslen"); else if (tag == "frame") _spos = xml.parseInt(); else if (tag == "file") { SndFileR wf = getWave(xml.parse1(), true); if (wf) f = wf; } else xml.unknown("Event"); break; case Xml::TagEnd: if (tag == "event") { Pos::setType(FRAMES); // DEBUG return; } default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- //void WaveEventBase::write(int level, Xml& xml, const Pos& offset) const void WaveEventBase::write(int level, Xml& xml, const Pos& offset, bool forcePath) const { if (f.isNull()) return; xml.tag(level++, "event"); PosLen wpos(*this); wpos += offset; wpos.write(level, xml, "poslen"); xml.intTag(level, "frame", _spos); // offset in wave file // // waves in the project dirctory are stored // with relative path name, others with absolute path // QString path = f.dirPath(); if (!forcePath && path.contains(MusEGlobal::museProject)) { // extract MusEGlobal::museProject. QString newName = f.path().remove(MusEGlobal::museProject+"/"); xml.strTag(level, "file", newName); } else xml.strTag(level, "file", f.path()); xml.etag(level, "event"); } void WaveEventBase::readAudio(WavePart* /*part*/, unsigned offset, float** buffer, int channel, int n, bool /*doSeek*/, bool overwrite) { // Added by Tim. p3.3.17 #ifdef WAVEEVENT_DEBUG_PRC printf("WaveEventBase::readAudio audConv:%p sfCurFrame:%ld offset:%u channel:%d n:%d\n", audConv, sfCurFrame, offset, channel, n); #endif // DELETETHIS 270. all the below stuff hasn't changed since revision 462, and // will not compile, and has a TODO in it. // will this ever be done, or is it completely obsolete? // even if we keep the #ifdef branch, there's a huge // comment in it. delete that? // Changed by Tim. p3.3.18 #ifdef USE_SAMPLERATE // TODO: >>>>>>>>>>>+++++++++++++++++++++++++++++ // If we have a valid audio converter then use it to do the processing. Otherwise just a normal seek + read. if(audConv) //sfCurFrame = audConv->process(f, sfCurFrame, offset + _spos, buffer, channel, n, doSeek, overwrite); DELETETHIS sfCurFrame = audConv->readAudio(f, sfCurFrame, offset, buffer, channel, n, doSeek, overwrite); else { if(!f.isNull()) { sfCurFrame = f.seek(offset + _spos, 0); sfCurFrame += f.read(channel, buffer, n, overwrite); } } return; /* DELETETHIS 250 unsigned fsrate = f.samplerate(); int fchan = f.channels(); off_t frame = offset + _spos; //bool resample = src_state && ((unsigned)sampleRate != fsrate); bool resample = audConv && audConv->isValid() && ((unsigned)sampleRate != fsrate); // Is a 'transport' seek requested? (Not to be requested with every read! Should only be for 'first read' seeks, or positional 'transport' seeks.) // Due to the support of sound file references in MusE, seek must ALWAYS be done before read, as before, // except now we alter the seek position if sample rate conversion is being used and remember the seek positions. if(doSeek) { if(!resample) { // Sample rates are the same. Just a regular seek, no conversion. sfCurFrame = f.seek(frame, 0); } else { // Sample rates are different. Seek to a calculated 'sample rate ratio factored' position. double srcratio = (double)fsrate / (double)sampleRate; //long inSize = long((double)frames * _src_ratio) + 1 // From MusE-2 file converter. off_t newfr = (off_t)floor(((double)frame * srcratio)); // From simplesynth. //_sfCurFrame = sf_seek(sf, newfr, 0); sfCurFrame = f.seek(newfr, 0); // Added by Tim. p3.3.17 #ifdef WAVEEVENT_DEBUG_PRC printf("WaveEventBase::readAudio Seek frame:%ld converted to frame:%ld _sfCurFrame:%ld\n", frame, newfr, sfCurFrame); #endif // Reset the src converter. It's current state is meaningless now. //int srcerr = src_reset(src_state); int srcerr = audConv->reset(); if(srcerr != 0) printf("WaveEventBase::readAudio Converter reset failed: %s\n", src_strerror(srcerr)); } } else { // No seek requested. Are the rates the same? if(!resample) // Sample rates are the same. Just a regular seek, no conversion. sfCurFrame = f.seek(frame, 0); else { // Added by Tim. p3.3.17 #ifdef WAVEEVENT_DEBUG_PRC printf("WaveEventBase::readAudio No 'transport' seek, rates different. Seeking to _sfCurFrame:%ld\n", sfCurFrame); #endif // Sample rates are different. We can't just tell seek to go to an absolute calculated position, // since the last position can vary - it might not be what the calculated position is. // We must use the last position left by SRC conversion, ie. let the file position progress on its own. sfCurFrame = f.seek(sfCurFrame, 0); } } // Do we not need to resample? if(!resample) { return sfCurFrame + f.read(channel, buffer, n, overwrite); } size_t rn; if((sampleRate == 0) || (fsrate == 0)) { if(debugMsg) printf("WaveEventBase::readAudio Using SRC: Error: sampleRate or file samplerate is zero!\n"); return sfCurFrame; } // Ratio is defined as output sample rate over input samplerate. double srcratio = (double)sampleRate / (double)fsrate; long outFrames = n; //long outSize = outFrames * channel; long outSize = outFrames * fchan; //long inSize = long(outSize * srcratio) + 1 // From MusE-2 file converter. //long inSize = (long)floor(((double)outSize / srcratio)); // From simplesynth. //long inFrames = (long)floor(((double)outFrames / srcratio)); // From simplesynth. long inFrames = (long)ceil(((double)outFrames / srcratio)); // From simplesynth. //long inFrames = (long)floor(double(outFrames * sfinfo.samplerate) / double(sampleRate)); // From simplesynth. // Extra input compensation - sometimes src requires more input frames than expected in order to // always get a reliable number of used out frames ! //inFrames = inFrames / (srcratio / 2.0); long inComp = 10; inFrames += inComp; long inSize = inFrames * fchan; //long inSize = inFrames * channel; float inbuffer[inSize]; float outbuffer[outSize]; //float* poutbuf; // If the number of file channels is the same as the process channels AND we want overwrite, we can get away with direct copying. //if(overwrite && channel == fchan) // Point the out buffer directly at the return buffers. // poutbuf = buffer; //else // Point the out buffer at our local buffers. // poutbuf = &outbuffer[0]; // Converter channels are fixed at creation time! Can't change them on the fly. Can't use 'channel' paramter. //rn = f.read(inbuffer, inFrames); rn = f.readDirect(inbuffer, inFrames); // convert SRC_DATA srcdata; srcdata.data_in = inbuffer; srcdata.data_out = outbuffer; //srcdata.data_out = poutbuf; //srcdata.input_frames = inSize; srcdata.input_frames = rn; srcdata.output_frames = outFrames; srcdata.end_of_input = ((long)rn != inFrames); srcdata.src_ratio = srcratio; #ifdef WAVEEVENT_DEBUG_PRC printf("WaveEventBase::readAudio %s processing converter... inFrames:%ld inSize:%ld outFrames:%ld outSize:%ld rn:%d", f.name().toLatin1(), inFrames, inSize, outFrames, outSize, rn); #endif //int srcerr = src_process(src_state, &srcdata); int srcerr = audConv->process(&srcdata); if(srcerr != 0) { printf("\nWaveEventBase::readAudio SampleRate converter process failed: %s\n", src_strerror(srcerr)); return sfCurFrame += rn; } #ifdef WAVEEVENT_DEBUG_PRC printf(" frames used in:%ld out:%ld\n", srcdata.input_frames_used, srcdata.output_frames_gen); #endif // If the number of frames read by the soundfile equals the input frames, go back. // Otherwise we have reached the end of the file, so going back is useless since // there shouldn't be any further calls. (Definitely get buffer underruns if further calls!) if((long)rn == inFrames) { // Go back by the amount of unused frames. sf_count_t seekn = inFrames - srcdata.input_frames_used; if(seekn != 0) { #ifdef WAVEEVENT_DEBUG_PRC printf("WaveEventBase::readAudio Seek-back by:%d\n", seekn); #endif sfCurFrame = f.seek(-seekn, SEEK_CUR); } else sfCurFrame += rn; } else sfCurFrame += rn; if(debugMsg) { if(srcdata.output_frames_gen != outFrames) printf("WaveEventBase::readAudio %s output_frames_gen:%ld != outFrames:%ld outSize:%ld inFrames:%ld srcdata.input_frames_used:%ld inSize:%ld rn:%d\n", f.name().toLatin1(), srcdata.output_frames_gen, outFrames, outSize, inFrames, srcdata.input_frames_used, inSize, rn); } if(inFrames != (long)rn) { if(debugMsg) printf("WaveEventBase::readAudio %s rn:%zd != inFrames:%ld output_frames_gen:%ld outFrames:%ld outSize:%ld srcdata.input_frames_used:%ld inSize:%ld\n", f.name().toLatin1(), rn, inFrames, srcdata.output_frames_gen, outFrames, outSize, srcdata.input_frames_used, inSize); // We've reached the end of the file. Convert the number of frames read. //rn = (double)rn * srcratio + 1; rn = (long)floor((double)rn * srcratio); if(rn > (size_t)outFrames) rn = outFrames; } else if(srcdata.output_frames_gen != outFrames) { // SRC didn't give us the number of frames we requested. // This can occasionally be radically different from the requested frames, or zero, // even when ample excess input frames are supplied. // We're not done converting yet - we haven't reached the end of the file. // We must do something with the buffer. So let's zero whatever SRC didn't fill. // FIXME: Instead of zeroing, try processing more input data until the out buffer is full. long b = srcdata.output_frames_gen * channel; long e = outFrames * channel; for(long i = b; i < e; ++i) outbuffer[i] = 0.0f; //poutbuf[i] = 0.0f; rn = outFrames; } else rn = outFrames; float* poutbuf = &outbuffer[0]; if(fchan == channel) { if(overwrite) for (size_t i = 0; i < rn; ++i) { for(int ch = 0; ch < channel; ++ch) *(buffer[ch] + i) = *poutbuf++; } else for(size_t i = 0; i < rn; ++i) { for(int ch = 0; ch < channel; ++ch) *(buffer[ch] + i) += *poutbuf++; } } else if((fchan == 2) && (channel == 1)) { // stereo to mono if(overwrite) for(size_t i = 0; i < rn; ++i) *(buffer[0] + i) = poutbuf[i + i] + poutbuf[i + i + 1]; else for(size_t i = 0; i < rn; ++i) *(buffer[0] + i) += poutbuf[i + i] + poutbuf[i + i + 1]; } else if((fchan == 1) && (channel == 2)) { // mono to stereo if(overwrite) for(size_t i = 0; i < rn; ++i) { float data = *poutbuf++; *(buffer[0]+i) = data; *(buffer[1]+i) = data; } else for(size_t i = 0; i < rn; ++i) { float data = *poutbuf++; *(buffer[0]+i) += data; *(buffer[1]+i) += data; } } else { if(debugMsg) printf("WaveEventBase::readAudio Channel mismatch: source chans:%d -> dst chans:%d\n", fchan, channel); } return sfCurFrame; */ #else if(f.isNull()) return; //sfCurFrame = f.seek(offset + _spos, 0); DELETETHIS 2 //sfCurFrame += f.read(channel, buffer, n, overwrite); off_t e_off = offset + _spos; if(e_off < 0) e_off = 0; f.seek(e_off, 0); f.read(channel, buffer, n, overwrite); return; #endif } } // namespace MusECore