/* SpiralSound * Copyleft (C) 2001 David Griffiths * * 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "SpiralLoopPlugin.h" #include "SpiralLoopPluginGUI.h" #include #include "SpiralIcon.xpm" #include "../../RiffWav.h" #include "../../NoteTable.h" static const float TRIG_THRESH = 0.1; static const int RECBUFFERSIZE = 16384; static const float RECORD_GAIN = 1.0f; extern "C" { SpiralPlugin* SpiralPlugin_CreateInstance() { return new SpiralLoopPlugin; } char** SpiralPlugin_GetIcon() { return SpiralIcon_xpm; } int SpiralPlugin_GetID() { return 0x001a; } string SpiralPlugin_GetGroupName() { return "Delay/Sampling"; } } /////////////////////////////////////////////////////// SpiralLoopPlugin::SpiralLoopPlugin() : m_Id(0), m_Pos(0), m_IntPos(0), m_PlayBufPos(0), m_Playing(true), m_Recording(false), m_DelMe(false), m_LoopPoint(0), m_Speed(1.0f), m_Volume(1.0f), m_RecordingSource(NULL), m_FirstRecord(true), m_FixedRecord(false), m_RecLength(0), m_TickTime(0), m_TickCurrent(0), m_TicksPerLoop(64), m_TickOutput(1.0f), m_Triggered(false) { m_PluginInfo.Name="SpiralLoop"; m_PluginInfo.Width=300; m_PluginInfo.Height=320; m_PluginInfo.NumInputs=2; m_PluginInfo.NumOutputs=10; m_PluginInfo.PortTips.push_back("Input"); m_PluginInfo.PortTips.push_back("Play Trigger"); m_PluginInfo.PortTips.push_back("Output"); m_PluginInfo.PortTips.push_back("Clock"); m_PluginInfo.PortTips.push_back("LoopTrigger 0"); m_PluginInfo.PortTips.push_back("LoopTrigger 1"); m_PluginInfo.PortTips.push_back("LoopTrigger 2"); m_PluginInfo.PortTips.push_back("LoopTrigger 3"); m_PluginInfo.PortTips.push_back("LoopTrigger 4"); m_PluginInfo.PortTips.push_back("LoopTrigger 5"); m_PluginInfo.PortTips.push_back("LoopTrigger 6"); m_PluginInfo.PortTips.push_back("LoopTrigger 7"); m_AudioCH->Register("TicksPerLoop",&m_TicksPerLoop); m_AudioCH->Register("Volume",&m_Volume); m_AudioCH->Register("Speed",&m_Speed); m_AudioCH->Register("Length",&m_GUIArgs.Length); m_AudioCH->Register("Start",&m_GUIArgs.Start); m_AudioCH->Register("End",&m_GUIArgs.End); m_AudioCH->Register("Pos",&m_Pos,ChannelHandler::OUTPUT); m_AudioCH->RegisterData("Name",ChannelHandler::INPUT,&m_GUIArgs.Name,sizeof(m_GUIArgs.Name)); m_AudioCH->RegisterData("SampleBuffer",ChannelHandler::OUTPUT_REQUEST,&m_SampleBuffer,TRANSBUF_SIZE); m_AudioCH->Register("SampleSize",&m_SampleSize,ChannelHandler::OUTPUT_REQUEST); m_Version=2; } SpiralLoopPlugin::~SpiralLoopPlugin() { } PluginInfo &SpiralLoopPlugin::Initialise(const HostInfo *Host) { return SpiralPlugin::Initialise(Host); } SpiralGUIType *SpiralLoopPlugin::CreateGUI() { return new SpiralLoopPluginGUI(m_PluginInfo.Width, m_PluginInfo.Height, this,m_AudioCH,m_HostInfo); } void SpiralLoopPlugin::Execute() { if (InputExists(0)) SetRecordingSource(GetInput(0)->GetBuffer()); else SetRecordingSource(NULL); for (int n=0; n<8; n++) GetOutputBuf(n+1)->Zero(); // get the triggers active this frame for (vector::iterator i=m_TriggerVec.begin(); i!=m_TriggerVec.end(); i++) { if (m_Pos > i->Time*m_StoreBuffer.GetLength() && !i->Triggered) { GetOutputBuf(i->Channel+2)->Set(1); i->Triggered=true; } } if (GetOutput(*GetOutputBuf(0))) { // if it's looped - reset the triggers for (vector::iterator i=m_TriggerVec.begin(); i!=m_TriggerVec.end(); i++) { i->Triggered=false; } m_TickCurrent=m_TickTime; } if (GetInput(1,0)>TRIG_THRESH) { if (!m_Triggered) { Trigger(); m_Triggered=true; } } else m_Triggered=false; m_TickCurrent+=m_HostInfo->BUFSIZE; if (m_TickCurrent>=m_TickTime) { m_TickOutput=-m_TickOutput; m_TickTime=m_StoreBuffer.GetLength()/m_TicksPerLoop; m_TickCurrent=0; } GetOutputBuf(1)->Set(m_TickOutput); } void SpiralLoopPlugin::ExecuteCommands() { if (m_AudioCH->IsCommandWaiting()) { switch(m_AudioCH->GetCommand()) { case START : SetPlaying(true); break; case STOP : SetPlaying(false); break; case RECORD : Clear(); Record(true); break; case OVERDUB : Record(true); break; case ENDRECORD : Record(false); break; case LOAD : LoadWav(m_GUIArgs.Name); break; case SAVE : SaveWav(m_GUIArgs.Name); break; case CUT : Cut(m_GUIArgs.Start, m_GUIArgs.End); break; case COPY : Copy(m_GUIArgs.Start, m_GUIArgs.End); break; case PASTE : Paste(m_GUIArgs.Start); break; case PASTEMIX : PasteMix(m_GUIArgs.Start); break; case ZERO_RANGE : ZeroRange(m_GUIArgs.Start, m_GUIArgs.End); break; case REVERSE_RANGE : ReverseRange(m_GUIArgs.Start, m_GUIArgs.End); break; case SELECT_ALL : SelectAll(); break; case DOUBLE : Double(); break; case HALF : Halve(); break; case MOVE : Move(m_GUIArgs.Start); break; case CROP : Crop(); break; case KEEPDUB : MixDub(); break; case UNDODUB : ClearDub(); break; case CHANGE_LENGTH : m_LoopPoint=(int)(m_StoreBuffer.GetLength()*m_GUIArgs.Length); break; case NEW_TRIGGER : { TriggerInfo NewTrigger; NewTrigger.Channel = m_GUIArgs.End; NewTrigger.Time = m_GUIArgs.Length; if ((int)m_TriggerVec.size()!=m_GUIArgs.Start) cerr<<"no of triggers error!"<SetupBulkTransfer((void*)m_StoreBuffer.GetBuffer()); m_SampleSize=m_StoreBuffer.GetLength(); } break; } } } void SpiralLoopPlugin::StreamOut(ostream &s) { s<::iterator i=m_TriggerVec.begin(); i!=m_TriggerVec.end(); i++) { s<Channel<<" "<Time<<" "; } } void SpiralLoopPlugin::StreamIn(istream &s) { int version; s>>version; s>>m_LoopPoint>>m_Speed>>m_Volume>>m_TicksPerLoop; int size; s>>size; for (int n=0; n>t.Channel>>t.Time; m_TriggerVec.push_back(t); } } bool SpiralLoopPlugin::SaveExternalFiles(const string &Dir) { char temp[256]; sprintf(temp,"%sSpiralLoopSample%d.wav",Dir.c_str(),SpiralPlugin_GetID()); SaveWav(temp); return true; } void SpiralLoopPlugin::LoadExternalFiles(const string &Dir) { char temp[256]; sprintf(temp,"%sSpiralLoopSample%d.wav",Dir.c_str(),SpiralPlugin_GetID()); LoadWav(temp); } void SpiralLoopPlugin::LoadWav(const char *Filename) { WavFile wav; if (wav.Open(Filename, WavFile::READ)) { //Clear(); AllocateMem(wav.GetSize()); wav.Load(m_StoreBuffer); } } void SpiralLoopPlugin::SaveWav(const char *Filename) { WavFile wav; if (wav.Open(Filename, WavFile::WRITE, WavFile::MONO)) { wav.Save(m_StoreBuffer); } m_Sample=Filename; } bool SpiralLoopPlugin::GetOutput(Sample &data) { if (!m_Recording && !m_Playing) { return false; } if (!m_Recording && m_StoreBuffer.GetLength()==0) { return false; } if (m_FixedRecord && m_DubBuffer.GetLength()!=m_StoreBuffer.GetLength()) { cerr<<"eek! dub and store buffers don't match!"<(m_Pos); // brute force fix if (Pos>0 && Pos(m_Pos)>=m_LoopPoint) { ret=true; m_Pos=0; } } m_IntPos=static_cast(m_Pos); return ret; } void SpiralLoopPlugin::AllocateMem(int Length) { // We might need to keep these values (if loading workspace) if (m_LoopPoint>Length) m_LoopPoint=Length; if (m_Pos>Length) m_Pos=0; if (m_LoopPoint==0) m_LoopPoint=Length; if (!m_StoreBuffer.Allocate(Length) || !m_DubBuffer.Allocate(Length)) { cerr<<"AllocateMem can't allocate any more memory!"<=m_StoreBuffer.GetLength()) { Pos=0; } for (int n=0; n(Pos)]+m_RecordingSource[n]*RECORD_GAIN; // fill in all the samples between the speed jump with the same value m_DubBuffer.Set((int)Pos,temp); for (int i=static_cast(OldPos); i<=static_cast(Pos); i++) { m_DubBuffer.Set(i,temp); } OldPos=Pos; Pos+=m_Speed; if (Pos>=m_StoreBuffer.GetLength()) { Pos-=m_StoreBuffer.GetLength(); // remember to fill up to the end of the last buffer for (int i=static_cast(OldPos); i=RECBUFFERSIZE) { // put the two buffers together m_StoreBuffer.Add(m_RecBuffer); m_RecPos=0; } m_RecBuffer.Set(m_RecPos,m_RecordingSource[n]*RECORD_GAIN); m_RecLength++; m_RecPos++; } } } void SpiralLoopPlugin::EndRecordBuf() { m_FirstRecord=true; m_LoopPoint=m_StoreBuffer.GetLength(); if (!m_FixedRecord) { // reallocate the hold buffer for the new size // (if the size has changed) m_DubBuffer.Allocate(m_LoopPoint); } } void SpiralLoopPlugin::Crop() { if (m_LoopPointLen) { SetLength(Len); return; } else { // if it's empty if (!m_StoreBuffer.GetLength()) { AllocateMem(Len); m_StoreBuffer.Zero(); } else // there is something in the buffer already, but we need to // add on some extra data to make the length the same { int ExtraLen=Len-m_StoreBuffer.GetLength(); m_StoreBuffer.Expand(ExtraLen); m_DubBuffer.Expand(ExtraLen); } } } void SpiralLoopPlugin::Cut(int Start, int End) { m_StoreBuffer.GetRegion(m_CopyBuffer,Start,End); m_StoreBuffer.Remove(Start,End); if (m_StoreBuffer.GetLength()m_LoopPoint) { m_Pos=0; } m_DubBuffer.Allocate(m_StoreBuffer.GetLength()); } void SpiralLoopPlugin::Copy(int Start, int End) { m_StoreBuffer.GetRegion(m_CopyBuffer,Start,End); } void SpiralLoopPlugin::Paste(int Start) { m_StoreBuffer.Insert(m_CopyBuffer,Start); if (m_StoreBuffer.GetLength()m_LoopPoint) { m_Pos=0; } m_DubBuffer.Allocate(m_StoreBuffer.GetLength()); } void SpiralLoopPlugin::PasteMix(int Start) { m_StoreBuffer.Mix(m_CopyBuffer,Start); } void SpiralLoopPlugin::ZeroRange(int Start, int End) { for (int n=Start; nm_LoopPoint) { m_Pos=0; } } void SpiralLoopPlugin::SelectAll() { } void SpiralLoopPlugin::Move(int Start) { m_StoreBuffer.Move(Start); }