|
- /*
- Copyright (C) 2006-2011 Nasca Octavian Paul
- Author: Nasca Octavian Paul
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License
- as published by the Free Software Foundation.
-
- 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 (version 2) for more details.
-
- You should have received a copy of the GNU General Public License (version 2)
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- #pragma once
-
- #include <string>
-
- #include "../JuceLibraryCode/JuceHeader.h"
-
- #include "InputS.h"
- #include <mutex>
-
- inline double ramp(int64_t pos, int64_t totallen, int64_t rampinlen, int64_t rampoutlen)
- {
- if (totallen < rampinlen + rampoutlen)
- return 1.0;
- if (pos < rampinlen)
- return 1.0 / rampinlen*pos;
- if (pos >= totallen - rampoutlen)
- return 1.0 - 1.0 / rampoutlen*(pos - totallen + rampoutlen);
- return 1.0;
- }
-
- class AInputS final : public InputS
- {
- public:
- AInputS(AudioFormatManager* mana) : m_manager(mana)
- {
- m_readbuf.setSize(2, 65536*2);
- m_readbuf.clear();
- m_crossfadebuf.setSize(2, 44100);
- m_crossfadebuf.clear();
- PlayRangeEndCallback=[](AInputS*){};
- }
- ~AInputS() {}
-
- void setAudioBuffer(AudioBuffer<float>* buf, int samplerate, int len)
- {
- ScopedLock locker(m_mutex);
- m_afreader = nullptr;
- m_using_memory_buffer = true;
- m_readbuf = *buf;
- info.nchannels = buf->getNumChannels();
- info.nsamples = len;
- info.samplerate = samplerate;
- m_currentsample = 0;
- m_loop_enabled = true;
- m_crossfadebuf.setSize(info.nchannels, m_crossfadebuf.getNumSamples());
- m_cached_file_range = { 0,len };
- seek(m_activerange.getStart(), true);
- updateXFadeCache();
- }
- virtual AudioBuffer<float>* getAudioBuffer() override
- {
- if (m_using_memory_buffer)
- return &m_readbuf;
- return nullptr;
- }
- bool openAudioFile(File file) override
- {
- m_silenceoutputted = 0;
- AudioFormatReader* reader = m_manager->createReaderFor(file);
- if (reader)
- {
- ScopedLock locker(m_mutex);
- m_using_memory_buffer = false;
- m_afreader = std::unique_ptr<AudioFormatReader>(reader);
- if (m_activerange.isEmpty())
- m_activerange = { 0.0,1.0 };
- m_currentsample = m_activerange.getStart()*info.nsamples;
- info.samplerate = (int)m_afreader->sampleRate;
- info.nchannels = m_afreader->numChannels;
- info.nsamples = m_afreader->lengthInSamples;
- if (m_readbuf.getNumChannels() < info.nchannels)
- {
- m_readbuf.setSize(info.nchannels, m_readbuf.getNumSamples());
- m_crossfadebuf.setSize(info.nchannels, m_crossfadebuf.getNumSamples());
- }
- updateXFadeCache();
- m_readbuf.clear();
- return true;
- }
- return false;
- }
- void close() override
- {
- m_afreader = nullptr;
- m_currentsample = 0;
- info.nchannels = 0;
- info.nsamples = 0;
- info.samplerate = 0;
- }
- int readNextBlock(AudioBuffer<float>& abuf, int nsmps, int numchans) override
- {
- ScopedLock locker(m_mutex);
- if (m_afreader == nullptr && m_using_memory_buffer == false)
- {
- jassert(false);
- return 0;
- }
- int inchans = 0;
- if (m_afreader)
- inchans = m_afreader->numChannels;
- else inchans = m_readbuf.getNumChannels();
- int64_t subsect_t0 = 0;
- int64_t subsect_t1 = 0;
- int64_t subsectlen = 0;
- int xfadelen = 0;
- auto updatesamplepositions = [&,this]()
- {
- subsect_t0 = (int64_t)(m_activerange.getStart()*info.nsamples);
- subsect_t1 = (int64_t)(m_activerange.getEnd()*info.nsamples);
- subsectlen = subsect_t1 - subsect_t0;
- xfadelen = m_xfadelen;
- if (xfadelen >= subsectlen)
- xfadelen = int(subsectlen - 2);
- };
- updatesamplepositions();
- auto getSampleLambda=[this](int64_t pos, int ch)
- {
- if (m_cached_file_range.contains(pos))
- return m_readbuf.getSample(ch, int(pos - m_cached_file_range.getStart()));
- else
- {
- Range<int64_t> activerange((int64_t)(m_activerange.getStart()*info.nsamples),
- (int64_t)(m_activerange.getEnd()*info.nsamples+1));
- Range<int64_t> possiblerange(pos, pos + m_readbuf.getNumSamples() + 0);
- m_cached_file_range = activerange.getIntersectionWith(possiblerange);
- m_afreader->read(&m_readbuf, 0, (int)m_cached_file_range.getLength(), pos, true, true);
- m_disk_read_count += m_cached_file_range.getLength()*m_afreader->numChannels;
- return m_readbuf.getSample(ch, int(pos - m_cached_file_range.getStart()));
- }
- };
- auto getCrossFadedSampleLambda=[this,&getSampleLambda](int64_t playpos, int chan, int64_t subt0, int64_t subt1, int xfadelen)
- {
- if (m_loop_enabled == false && playpos >= subt1)
- {
- return 0.0f;
- }
- if (playpos >= subt0 && playpos <= subt1 - xfadelen)
- return getSampleLambda(playpos, chan);
- if (playpos > (subt1 - xfadelen) && playpos<subt1)
- {
- int64_t fadeindex = playpos - subt1 + xfadelen;
- double fadeoutgain = 1.0 - (1.0 / (xfadelen - 0))*fadeindex;
- float s0 = (float)(getSampleLambda(playpos, chan)*fadeoutgain);
- double fadeingain = (1.0 / (xfadelen - 0))*fadeindex;
- int64_t playpos2 = playpos - subt1 + xfadelen;
- jassert(playpos2>=0 && playpos2<=xfadelen);
- float s1 = (float)(m_crossfadebuf.getSample(chan, (int)playpos2)*fadeingain);
- return s0 + s1;
- }
- ++m_silenceoutputted;
- return 0.0f;
- };
- float** smps = abuf.getArrayOfWritePointers();
- int readinc = 1;
- if (m_reverseplay)
- readinc = -1;
- for (int i = 0; i < nsmps; ++i)
- {
- float seekfadegain = 1.0f;
- if (m_seekfade.state == 1)
- {
- //Logger::writeToLog("Seek requested to pos " + String(m_seekfade.requestedpos));
- m_seekfade.state = 2;
- }
- if (m_seekfade.state == 2)
- {
- seekfadegain = 1.0 - (1.0 / m_seekfade.length*m_seekfade.counter);
- ++m_seekfade.counter;
- if (m_seekfade.counter >= m_seekfade.length)
- {
- //Logger::writeToLog("Doing seek " + String(m_seekfade.requestedpos));
- m_seekfade.counter = 0;
- m_seekfade.state = 3;
- if (m_seekfade.requestedrange.isEmpty() == false)
- {
- setActiveRangeImpl(m_seekfade.requestedrange);
- updatesamplepositions();
- if (m_activerange.contains(getCurrentPositionPercent()) == false)
- seekImpl(m_activerange.getStart());
- }
-
- }
- }
- if (m_seekfade.state == 3)
- {
- seekfadegain = 1.0 / m_seekfade.length*m_seekfade.counter;
- ++m_seekfade.counter;
- if (m_seekfade.counter >= m_seekfade.length)
- {
- //Logger::writeToLog("Seek cycle finished");
- m_seekfade.counter = 0;
- m_seekfade.state = 0;
- m_seekfade.requestedpos = 0.0;
- m_seekfade.requestedrange = Range<double>();
- }
- }
- jassert(seekfadegain >= 0.0f && seekfadegain<=1.0f);
- if (inchans == 1 && numchans > 0)
- {
- float sig = getCrossFadedSampleLambda(m_currentsample, 0, subsect_t0, subsect_t1, xfadelen);
- for (int j = 0; j < numchans; ++j)
- {
- smps[j][i] = sig*seekfadegain;
- }
- }
- else if (inchans > 1 && numchans > 1)
- {
- for (int j = 0; j < numchans; ++j)
- {
- int inchantouse = j % inchans;
- smps[j][i] = seekfadegain*getCrossFadedSampleLambda(m_currentsample, inchantouse,
- subsect_t0, subsect_t1,xfadelen);
- }
-
- }
- m_currentsample += readinc;
- if (m_loop_enabled == true)
- {
- if (m_reverseplay == false && m_currentsample >= subsect_t1)
- {
- m_currentsample = subsect_t0+xfadelen;
- ++m_loopcount;
- }
- else if (m_reverseplay == true && m_currentsample < subsect_t0)
- {
- m_currentsample = subsect_t1 - 1;
- }
- } else
- {
- if (m_reverseplay == false && m_currentsample == subsect_t1)
- PlayRangeEndCallback(this);
- else if (m_reverseplay == true && m_currentsample == subsect_t0)
- PlayRangeEndCallback(this);
- }
- }
-
- return nsmps;
- }
- void seekImpl(double pos)
- {
- if (m_using_memory_buffer == true)
- {
- jassert(m_readbuf.getNumSamples() > 0 && m_afreader == nullptr);
- m_loopcount = 0;
- m_silenceoutputted = 0;
- m_cache_misses = 0;
- m_currentsample = (int64_t)(pos*m_readbuf.getNumSamples());
- m_currentsample = jlimit<int64_t>(0, m_readbuf.getNumSamples(), m_currentsample);
- m_cached_file_range = { 0,m_readbuf.getNumSamples() };
- return;
- }
- //jassert(m_afreader!=nullptr);
- if (m_afreader == nullptr)
- return;
- m_loopcount = 0;
- m_silenceoutputted = 0;
- m_cache_misses = 0;
- m_currentsample = (int64_t)(pos*m_afreader->lengthInSamples);
- m_currentsample = jlimit<int64_t>(0, m_afreader->lengthInSamples, m_currentsample);
- //Logger::writeToLog("Seeking to " + String(m_currentsample));
- //if (m_cached_file_range.contains(info.currentsample)==false)
- m_cached_file_range = Range<int64_t>();
- updateXFadeCache();
- //m_cached_crossfade_range = Range<int64_t>();
- }
- void seek(double pos, bool immediate) override //0=start,1.0=end
- {
- ScopedLock locker(m_mutex);
- if (immediate == true)
- {
- seekImpl(pos);
- return;
- }
- if (m_seekfade.state == 0)
- {
- m_seekfade.state = 1;
- m_seekfade.counter = 0;
- }
- m_seekfade.length = 16384;
- m_seekfade.requestedpos = pos;
-
- }
-
- std::pair<Range<double>,Range<double>> getCachedRangesNormalized()
- {
- if (m_afreader == nullptr)
- return {};
- return { { jmap<double>((double)m_cached_file_range.getStart(),0,(double)info.nsamples,0.0,1.0),
- jmap<double>((double)m_cached_file_range.getEnd(), 0, (double)info.nsamples, 0.0, 1.0) },
- { jmap<double>((double)m_cached_crossfade_range.getStart(),0,(double)info.nsamples,0.0,1.0),
- jmap<double>((double)m_cached_crossfade_range.getEnd(), 0, (double)info.nsamples, 0.0, 1.0) } };
- }
- int getNumCacheMisses() { return m_cache_misses; }
- void updateXFadeCache()
- {
- if (m_xfadelen>m_crossfadebuf.getNumSamples())
- m_crossfadebuf.setSize(info.nchannels,m_xfadelen);
- if (m_afreader != nullptr && m_using_memory_buffer == false)
- m_afreader->read(&m_crossfadebuf, 0, m_xfadelen, (int64_t)(m_activerange.getStart()*info.nsamples), true, true);
- if (m_afreader == nullptr && m_using_memory_buffer == true)
- {
- for (int i=0;i<info.nchannels;++i)
- m_crossfadebuf.copyFrom(i, 0, m_readbuf, i, (int64_t)(m_activerange.getStart()*info.nsamples), m_xfadelen);
- }
- m_cached_crossfade_range =
- Range<int64_t>((int64_t)(m_activerange.getStart()*info.nsamples),(int64_t)(m_activerange.getStart()*info.nsamples+m_xfadelen));
- }
- void setActiveRangeImpl(Range<double> rng)
- {
- if (rng.getEnd() < rng.getStart())
- rng = { 0.0,1.0 };
- if (rng.isEmpty())
- rng = { 0.0,1.0 };
- m_activerange = rng;
- m_loopcount = 0;
- updateXFadeCache();
- }
- void setActiveRange(Range<double> rng) override
- {
- ScopedLock locker(m_mutex);
-
- /*
- if (rng.contains(getCurrentPositionPercent()))
- {
- setActiveRangeImpl(rng);
- return;
- }
- */
- m_seekfade.requestedrange = rng;
- if (m_seekfade.state == 0)
- {
- m_seekfade.counter = 0;
- m_seekfade.state = 1;
- }
- else
- {
- m_seekfade.state = 0;
- setActiveRangeImpl(rng);
- }
- m_seekfade.length = 2048;
- }
- void setLoopEnabled(bool b) override
- {
- m_loop_enabled = b;
- m_loopcount = 0;
- updateXFadeCache();
- }
- void setXFadeLenSeconds(double len)
- {
- if (info.samplerate==0)
- return;
- len = jlimit(0.0,1.0,len);
- int temp = (int)(len*info.samplerate);
- if (m_xfadelen!=temp)
- {
- m_xfadelen = temp;
- updateXFadeCache();
- }
- }
- Range<int64_t> getActiveRangeFrames()
- {
- if (info.nsamples == 0)
- return Range<int64_t>();
- return Range<int64_t>((int64_t)(m_activerange.getStart()*info.nsamples), (int64_t)(m_activerange.getEnd()*info.nsamples));
- }
- void setReversePlay(bool b)
- {
- m_reverseplay = b;
- }
- bool isReversed() { return m_reverseplay; }
- int64_t getLoopCount() { return m_loopcount; }
-
- private:
- std::function<void(AInputS*)> PlayRangeEndCallback;
- std::unique_ptr<AudioFormatReader> m_afreader;
- AudioBuffer<float> m_readbuf;
- AudioBuffer<float> m_crossfadebuf;
- Range<int64_t> m_cached_file_range;
- Range<int64_t> m_cached_crossfade_range;
- int m_cache_misses = 0;
- int m_fade_in = 512;
- int m_fade_out = 512;
- int m_xfadelen = 0;
- bool m_reverseplay = false;
- int64_t m_loopcount = 0;
- bool m_using_memory_buffer = true;
- AudioFormatManager* m_manager = nullptr;
- CriticalSection m_mutex;
- struct
- {
- int state = 0; // 0 inactive, 1 seek requested, 2 fade out, 3 fade in
- int counter = 0;
- int length = 44100;
- double requestedpos = 0.0;
- Range<double> requestedrange;
- } m_seekfade;
- };
|