Loopp/src/looperclip.cxx

551 lines
11 KiB
C++

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "looperclip.hxx"
#include <stdio.h>
#include "config.hxx"
#include "jack.hxx"
#include "event.hxx"
#include "eventhandler.hxx"
#include "audiobuffer.hxx"
#include "controllerupdater.hxx"
#include "timemanager.hxx"
#include <math.h>
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<float> &vL = _buffer->getDataL();
std::vector<float> &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