/* * Author: Harry van Haaren 2013 * harryhaaren@gmail.com * * 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, either version 3 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, see . */ #include "looperclip.hxx" #include #include "config.hxx" #include "jack.hxx" #include "event.hxx" #include "eventhandler.hxx" #include "audiobuffer.hxx" #include "controllerupdater.hxx" #include "timemanager.hxx" #include extern Jack* jack; LooperClip::LooperClip(int t, int s) : Stately(), track(t), scene(s), TimeObserver() { _buffer = new AudioBuffer(LOOPER_SAMPLES_UPDATE_SIZE); init(); #ifdef DEBUG_BUFFER cout << "AudioBuffer " << _buffer->getID() << " has size (init): " << _buffer->getSize() << endl; #endif } void LooperClip::init() { _loaded = false; _playing = false; _recording = false; resetQueues(); if ( _buffer ) { _buffer->init(); } _newBufferInTransit = false; _recordhead = 0; _barsRecorded = 0; _playbackSpeed = 1; _nextPlaybackSpeed = 1; _playbackSpeedChange = false; resetPlayHead(); jack->getControllerUpdater()->setSceneState(track, scene, getState()); jack->getControllerUpdater()->setTrackSceneProgress( track, scene, getProgress()); } void LooperClip::save() { // ensure the buffer exists, and is saveable (not recording) if ( _loaded && !_recording && !_queueRecord ) { char buffer [50]; sprintf (buffer, "LC::save() track %i, scene %i", track,scene); EventGuiPrint e( buffer ); writeToGuiRingbuffer( &e ); int frames = _buffer->getAudioFrames(); EventRequestSaveBuffer e2( track, scene, frames ); writeToGuiRingbuffer( &e2 ); } else { // notify of "success" of save if there *is* no state to save Stately::success(); } } void LooperClip::reset() { if ( _recording ) { jack->subRecordingClip (); } if ( _loaded ) { jack->subRecordedClip (); } init (); } /// loads a sample: eg from disk, unloading current sample if necessary void LooperClip::load( AudioBuffer* ab ) { setStopped(); if ( _buffer ) { EventDeallocateBuffer e( _buffer ); writeToGuiRingbuffer( &e ); } _buffer = ab; // set the endpoint to the buffer's size _recordhead = _buffer->getSize(); _recFpb = _recordhead / _buffer->getBeats(); #ifdef DEBUG_BUFFER char buffer [50]; sprintf (buffer, "LC::load() t %i, s %i, aF %i",track, scene, int(_buffer->getAudioFrames()) ); EventGuiPrint e( buffer ); writeToGuiRingbuffer( &e ); #endif } void LooperClip::setRequestedBuffer( AudioBuffer* ab ) { if ( _buffer ) { size_t size = _buffer->getSize(); memcpy( &ab->getDataL().at(0), &_buffer->getDataL().at(0), sizeof(float)*size); memcpy( &ab->getDataR().at(0), &_buffer->getDataR().at(0), sizeof(float)*size); ab->setID ( _buffer->getID() ); ab->setBeats( _buffer->getBeats() ); EventDeallocateBuffer e( _buffer ); writeToGuiRingbuffer( &e ); } _buffer = ab; _newBufferInTransit = false; } void LooperClip::recieveSaveBuffer( AudioBuffer* saveBuffer ) { if ( saveBuffer->getSize() >= _buffer->getDataL().at(0) || saveBuffer->getSize() >= _buffer->getDataR().at(0) ) { // copy current contents into save buffer, // getData() contains L and R buffer, so twice the size is needed size_t framesBySize = _buffer->getAudioFrames(); memcpy( &saveBuffer->getDataL().at(0), &_buffer->getDataL().at(0), sizeof(float)*framesBySize); memcpy( &saveBuffer->getDataR().at(0), &_buffer->getDataR().at(0), sizeof(float)*framesBySize); saveBuffer->setID ( _buffer->getID() ); saveBuffer->setBeats( _buffer->getBeats() ); saveBuffer->setAudioFrames( _buffer->getAudioFrames() ); EventStateSaveBuffer e ( track, scene, saveBuffer ); writeToGuiRingbuffer( &e ); Stately::success(); } else { char buffer [50]; sprintf (buffer, "LC:: %i, %i: can't save, buf too small",track, scene ); EventGuiPrint e( buffer ); writeToGuiRingbuffer( &e ); Stately::error(""); } } void LooperClip::resetPlayHead() { if (!_recording) { _playhead = 0; _beatsPlayed = 0; _barsPlayed = 0; if(_playbackSpeedChange) { _playbackSpeed = _nextPlaybackSpeed; _playbackSpeedChange = false; } jack->getControllerUpdater()->setTrackSceneProgress( track, scene, getProgress()); } } void LooperClip::record(int count, float* L, float* R) { if (recordSpaceAvailable() < LOOPER_SAMPLES_BEFORE_REQUEST && !newBufferInTransit()) { requestNewBuffer(); } // write "count" samples into current buffer. if ( _buffer ) { size_t size = _buffer->getSize(); for(int i = 0; i < count; i++) { if ( _recordhead < size ) { _buffer->getDataL().at( _recordhead ) = *L++; _buffer->getDataR().at( _recordhead ) = *R++; _recordhead++; } else { // break: this is *BAD*, audio data is lost but the buffer isn't here // yet to hold new audio data so there's no option. This has not been // encountered in actual usage, only during the development process. char buffer [50]; sprintf (buffer, "LooperClip t %i, s %i, Error: out of mem!",track, scene ); EventGuiPrint e( buffer ); writeToGuiRingbuffer( &e ); #ifdef BUILD_TESTS LOOPP_WARN("%s","buffer has no space"); #endif break; } } } _loaded = true; if(!jack->getFreeRecMode()) { _recFpb = jack->getTimeManager()->getFpb(); } } unsigned long LooperClip::recordSpaceAvailable() { if ( _buffer ) return _buffer->getSize() - _recordhead; return 0; } size_t LooperClip::audioBufferSize() { if ( _buffer ) { return _buffer->getSize(); } return 0; } void LooperClip::setBeats(int beats) { if ( _buffer ) { _nextPlaybackSpeed = (long double)_buffer->getBeats() / (long double)beats; _playbackSpeedChange = true; } } int LooperClip::getBeats() { if ( _buffer ) return _buffer->getBeats(); return 0; } long LooperClip::getBufferLenght() { return _recordhead; } long LooperClip::getActualAudioLength() { return _buffer->getAudioFrames(); } void LooperClip::bar() { if(_playing) { _barsPlayed++; } if(_recording && !jack->getFreeRecMode()) { _barsRecorded++; } if(_queuePlay) { setPlaying(); } else if(_queueStop && _loaded) { setStopped(); } else if(_queueRecord) { setRecording(); } if(_recording && jack->getClipLength() > 0 && _barsRecorded == jack->getClipLength() - 1) { queuePlay(); } if(_recording && !jack->getFreeRecMode()) { // FIXME: assumes 4 beats in a bar _buffer->setBeats(_buffer->getBeats() + 4); _buffer->setAudioFrames( jack->getTimeManager()->getFpb() * _buffer->getBeats()); } } void LooperClip::beat() { if(_playing) { _beatsPlayed++; } if(_playing && _beatsPlayed >= (getBeats() / _playbackSpeed)) { resetPlayHead(); } } void LooperClip::resetQueues() { _queuePlay = false; _queueRecord = false; _queueStop = false; jack->getControllerUpdater ()->setSceneState ( track, scene, GridLogic::STATE_EMPTY ); } bool LooperClip::somethingQueued() { if ( _queuePlay || _queueStop || _queueRecord ) { return true; } return false; } void LooperClip::queuePlay() { if (_loaded && !somethingQueued()) { _queuePlay = true; _queueStop = false; _queueRecord = false; } jack->getControllerUpdater()->setSceneState(track, scene, getState()); } void LooperClip::queueStop() { if (_loaded && _playing && !somethingQueued()) { _queuePlay = false; _queueStop = true; _queueRecord = false; } jack->getControllerUpdater()->setSceneState(track, scene, getState()); } void LooperClip::queueRecord() { if (!somethingQueued()) { _queuePlay = false; _queueStop = false; _queueRecord = true; } jack->getControllerUpdater()->setSceneState(track, scene, getState()); } void LooperClip::setRecording() { _loaded = true; _playing = false; _recording = true; resetQueues(); _recordhead = 0; if ( _buffer ) { _buffer->setBeats( 0 ); } jack->getControllerUpdater()->setSceneState(track, scene, getState()); jack->addRecordedClip (); jack->addRecordingClip (); } void LooperClip::setPlaying() { if ( _recording ) { jack->subRecordingClip (); } if ( _loaded ) { _playing = true; _recording = false; resetQueues(); resetPlayHead(); } else { resetQueues(); } jack->getControllerUpdater()->setSceneState(track, scene, getState()); } void LooperClip::setStopped() { _loaded = true; _playing = false; _recording = false; resetQueues(); resetPlayHead(); // set "progress" to zero, as we're stopped! jack->getControllerUpdater()->setSceneState( track, scene, getState()); } GridLogic::State LooperClip::getState() { GridLogic::State s = GridLogic::STATE_EMPTY; if ( _loaded ) s = GridLogic::STATE_STOPPED; if ( _playing ) s = GridLogic::STATE_PLAYING; if ( _recording ) s = GridLogic::STATE_RECORDING; if ( _queuePlay ) s = GridLogic::STATE_PLAY_QUEUED; if ( _queueStop ) s = GridLogic::STATE_STOP_QUEUED; if ( _queueRecord ) s = GridLogic::STATE_RECORD_QUEUED; return s; } bool LooperClip::playing() { return _playing; } bool LooperClip::getQueueStop() { return _queueStop; } bool LooperClip::getQueuePlay() { return _queuePlay; } bool LooperClip::getLoaded() { return _loaded; } bool LooperClip::recording() { return _recording; } void LooperClip::requestNewBuffer() { EventLooperClipRequestBuffer e( track, scene, audioBufferSize() + LOOPER_SAMPLES_UPDATE_SIZE); writeToGuiRingbuffer( &e ); _newBufferInTransit = true; } bool LooperClip::newBufferInTransit() { return _newBufferInTransit; } void LooperClip::getSample(long double playSpeed, float *L, float *R) { playSpeed *= _playbackSpeed; if(_buffer && (_buffer->getSize() > 0)) { if(_playhead >= _recordhead || _playhead >= _buffer->getSize() || _playhead < 0) { // FIXME is there a better way than just output 0 if frames are missing? *L = 0.f; *R = 0.f; _playhead += playSpeed; return; EventGuiPrint e("LooperClip resetting _playhead"); writeToGuiRingbuffer( &e ); } std::vector &vL = _buffer->getDataL(); std::vector &vR = _buffer->getDataR(); *L = vL[_playhead + 0.5]; *R = vR[_playhead + 0.5]; _playhead += playSpeed; } else { *L = 0.f; *R = 0.f; } } float LooperClip::getProgress() { if ( _buffer && _playing ) { float p = float(_playhead) / _recordhead; return p; } return 0.f; } float LooperClip::getPlayhead() { return _playhead; } void LooperClip::processFreeRec() { setStopped(); int max_beats = (MAX_TEMPO * _recordhead) / (jack->getSamplerate() * 60); // calculate lower multiple of 4 int beats = (int)(max_beats/4)*4; // TODO 4 beats/bar if (beats < 1) beats = 1; _buffer->setBeats(beats); _buffer->setAudioFrames( _recordhead); _barsRecorded = beats / 4; // TODO 4 beats/bar _recFpb = _recordhead / beats; jack->getTimeManager()->setFpb(_recFpb); jack->getTimeManager()->setTransportState(TRANSPORT_ROLLING); queuePlay(); jack->setFreeRecMode(false); } #ifdef BUILD_TESTS void LooperClip::setState( bool load, bool play, bool rec, bool qPlay, bool qStop, bool qRec ) { _loaded = load; _playing = play; _recording = rec; _queuePlay = qPlay; _queueStop = qStop; _queueRecord = qRec; } #endif