| @@ -20,6 +20,7 @@ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <stdarg.h> | |||
| bool Pawfal_YesNo(const char *a,...) | |||
| { | |||
| @@ -53,7 +53,9 @@ m_CurrentNoteCV(0), | |||
| m_CurrentTriggerCV(0), | |||
| m_Triggered(false), | |||
| m_ClockHigh(false), | |||
| m_CopyPattern(0) | |||
| m_CopyPattern(0), | |||
| m_PatAdvance(false), | |||
| m_PatReset(false) | |||
| { | |||
| m_Version=3; | |||
| @@ -67,6 +69,8 @@ m_CopyPattern(0) | |||
| m_PluginInfo.PortTips.push_back("Input Pitch CV"); | |||
| m_PluginInfo.PortTips.push_back("Input Trigger CV"); | |||
| m_PluginInfo.PortTips.push_back("External Clock"); | |||
| //m_PluginInfo.PortTips.push_back("Pattern Advance"); | |||
| //m_PluginInfo.PortTips.push_back("Pattern Reset"); | |||
| m_PluginInfo.PortTips.push_back("Output Pitch"); | |||
| m_PluginInfo.PortTips.push_back("Output Trigger"); | |||
| m_PluginInfo.PortTips.push_back("Trigger 1"); | |||
| @@ -89,7 +93,7 @@ m_CopyPattern(0) | |||
| for (int n=0; n<NUM_PATTERNS; n++) | |||
| { | |||
| m_Matrix[n].Length=32; | |||
| m_Matrix[n].Length=64; | |||
| m_Matrix[n].Speed=1.0f; | |||
| m_Matrix[n].Octave=0; | |||
| @@ -149,7 +153,7 @@ void MatrixPlugin::Execute() | |||
| if (m_Step+1 >= m_Matrix[m_Current].Length) SetOutput(18, n, 1); | |||
| else SetOutput(18, n, 0); | |||
| if (GetInputPitch(0,n)>0) | |||
| { | |||
| if (!m_Triggered) | |||
| @@ -167,10 +171,7 @@ void MatrixPlugin::Execute() | |||
| } | |||
| // make it so the next note to trigger | |||
| // will be the first one | |||
| //if (m_GUI) ((MatrixPluginGUI*)m_GUI)->UpdateValues(); | |||
| // will be the first one | |||
| m_Time=m_StepTime*(1/m_Matrix[m_Current].Speed); | |||
| m_Step=-1; | |||
| @@ -216,9 +217,40 @@ void MatrixPlugin::Execute() | |||
| ExternalClockTriggered=true; | |||
| } | |||
| } | |||
| /* not yet... | |||
| // external pattern advance | |||
| if (GetInput(5,n)>0) | |||
| { | |||
| if (!m_PatAdvance) | |||
| { | |||
| m_Current++; | |||
| if (m_Current==16) m_Current=0; | |||
| m_PatAdvance=true; | |||
| m_Step=-1; | |||
| ExternalClockTriggered=true; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| m_PatAdvance=false; | |||
| } | |||
| // external pattern reset | |||
| if (GetInput(6,n)>0) | |||
| { | |||
| if (!m_PatReset) | |||
| { | |||
| m_Current=0; | |||
| m_PatReset=true; | |||
| m_Step=-1; | |||
| ExternalClockTriggered=true; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| m_PatReset=false; | |||
| } | |||
| */ | |||
| // An external clock pulse overrides the internal timing | |||
| if ((!ExternalClock && m_Time>=m_StepTime*(1/m_Matrix[m_Current].Speed)) || | |||
| (ExternalClock && ExternalClockTriggered)) | |||
| @@ -227,9 +259,7 @@ void MatrixPlugin::Execute() | |||
| m_Step++; | |||
| if (m_Step >= m_Matrix[m_Current].Length) m_Step=0; | |||
| //if (m_GUI) ((MatrixPluginGUI*)m_GUI)->SetLED(m_Step); | |||
| // Reset the values | |||
| m_CurrentTriggerCV=0; | |||
| if (m_NoteCut) m_CurrentNoteCV=0; | |||
| @@ -239,7 +269,6 @@ void MatrixPlugin::Execute() | |||
| m_TriggerLevel[t]=0; | |||
| } | |||
| // Scan the matrix at current time | |||
| for (int i=0; i<MATY; i++) | |||
| { | |||
| @@ -271,7 +300,7 @@ void MatrixPlugin::ExecuteCommands() | |||
| break; | |||
| case MAT_SPEED : | |||
| m_Matrix[m_Current].Speed=m_GUIArgs.Speed; | |||
| m_Matrix[m_Current].Speed=m_GUIArgs.Speed/8.0f; | |||
| break; | |||
| case MAT_ACTIVATE : | |||
| @@ -95,6 +95,8 @@ private: | |||
| bool m_Triggered; | |||
| bool m_ClockHigh; | |||
| int m_CopyPattern; | |||
| bool m_PatAdvance; | |||
| bool m_PatReset; | |||
| }; | |||
| #endif | |||
| @@ -87,7 +87,8 @@ int Fl_MatrixButton::handle(int event) | |||
| //////////////////////////////////////////// | |||
| MatrixPluginGUI::MatrixPluginGUI(int w, int h,MatrixPlugin *o,ChannelHandler *ch,const HostInfo *Info) : | |||
| SpiralPluginGUI(w,h,o,ch) | |||
| SpiralPluginGUI(w,h,o,ch), | |||
| m_LastLight(0) | |||
| { | |||
| //size_range(10,10); | |||
| m_NoteCut = new Fl_Button (5, h-30, 85, 20,"NoteCut"); | |||
| @@ -207,14 +208,13 @@ SpiralPluginGUI(w,h,o,ch) | |||
| void MatrixPluginGUI::Update() | |||
| { | |||
| for(int x=0; x<MATX; x++) | |||
| int Light=m_GUICH->GetInt("Step"); | |||
| if (Light!=m_LastLight) | |||
| { | |||
| m_Flash[x]->value(0); | |||
| m_Flash[Light]->value(1); | |||
| m_Flash[m_LastLight]->value(0); | |||
| m_LastLight=Light; | |||
| } | |||
| m_Flash[m_GUICH->GetInt("Step")]->value(1); | |||
| redraw(); | |||
| } | |||
| void MatrixPluginGUI::UpdateValues(SpiralPlugin *o) | |||
| @@ -233,21 +233,8 @@ void MatrixPluginGUI::UpdateValues(SpiralPlugin *o) | |||
| m_Matrix[x][y]->value(Plugin->GetPattern()->Matrix[x][y]); | |||
| m_Matrix[x][y]->SetVolume(Plugin->GetPattern()->Volume[x][y]); | |||
| } | |||
| //if (Plugin->CanTransposeUp()) m_TransUpBtn->activate(); else m_TransUpBtn->deactivate(); | |||
| //if (Plugin->CanTransposeDown()) m_TransDnBtn->activate(); else m_TransDnBtn->deactivate(); | |||
| } | |||
| /*void MatrixPluginGUI::SetLED(int n) | |||
| { | |||
| for (int i=0; i<MATX; i++) | |||
| { | |||
| m_Flash[i]->value(false); | |||
| } | |||
| m_Flash[n]->value(true); | |||
| }*/ | |||
| void MatrixPluginGUI::UpdateMatrix() | |||
| { | |||
| m_GUICH->Wait(); | |||
| @@ -307,10 +294,7 @@ inline void MatrixPluginGUI::cb_Length_i(Fl_Counter* o, void* v) | |||
| { | |||
| if (o->value()<1) o->value(1); | |||
| if (o->value()>64) o->value(64); | |||
| //m_GUICH->GetPattern()->Length=(int)o->value(); | |||
| cerr<<(int)o->value()<<endl; | |||
| m_GUICH->Set("Length",(int)o->value()); | |||
| m_GUICH->SetCommand(MatrixPlugin::MAT_LENGTH); | |||
| @@ -74,6 +74,7 @@ private: | |||
| int Numbers[MATX*MATY]; | |||
| Pattern m_GUIMatrix[NUM_PATTERNS]; | |||
| int m_LastLight; | |||
| Fl_Button* m_NoteCut; | |||
| Fl_Counter* m_Pattern; | |||
| @@ -60,7 +60,7 @@ int OutputPlugin::m_NoExecuted=0; | |||
| { \ | |||
| perror("Sound device did not accept settings"); \ | |||
| m_OutputOk=false; \ | |||
| return; \ | |||
| return false; \ | |||
| } | |||
| extern "C" | |||
| @@ -83,10 +83,11 @@ int GetID() | |||
| /////////////////////////////////////////////////////// | |||
| OutputPlugin::OutputPlugin() | |||
| OutputPlugin::OutputPlugin() : | |||
| m_Volume(1.0f) | |||
| { | |||
| m_RefCount++; | |||
| m_PluginInfo.Name="OSS"; | |||
| m_PluginInfo.Width=100; | |||
| m_PluginInfo.Height=130; | |||
| @@ -98,16 +99,17 @@ OutputPlugin::OutputPlugin() | |||
| m_PluginInfo.PortTips.push_back("Left In"); | |||
| m_PluginInfo.PortTips.push_back("Right In"); | |||
| m_AudioCH->Register("Mode",(char*)&m_Mode,ChannelHandler::INPUT); | |||
| m_AudioCH->Register("Volume",(char*)&m_Volume); | |||
| m_Mode=OUTPUT; | |||
| m_Mode=NO_MODE; | |||
| } | |||
| OutputPlugin::~OutputPlugin() | |||
| { | |||
| { | |||
| m_RefCount--; | |||
| if (m_RefCount==0) | |||
| { | |||
| { | |||
| cb_Blocking(m_Parent,false); | |||
| OSSOutput::PackUpAndGoHome(); | |||
| } | |||
| } | |||
| @@ -117,6 +119,7 @@ PluginInfo &OutputPlugin::Initialise(const HostInfo *Host) | |||
| PluginInfo& Info= SpiralPlugin::Initialise(Host); | |||
| host=Host; | |||
| OSSOutput::Get()->AllocateBuffer(); | |||
| return Info; | |||
| } | |||
| @@ -131,15 +134,18 @@ SpiralGUIType *OutputPlugin::CreateGUI() | |||
| void OutputPlugin::Execute() | |||
| { | |||
| // Only Play() once per set of plugins | |||
| m_NoExecuted++; | |||
| if (m_NoExecuted==m_RefCount) | |||
| { | |||
| if (m_Mode==INPUT || m_Mode==DUPLEX) OSSOutput::Get()->Read(); | |||
| if (m_Mode==OUTPUT || m_Mode==DUPLEX) OSSOutput::Get()->Play(); | |||
| m_NoExecuted=0; | |||
| if (m_Mode==NO_MODE) | |||
| { | |||
| if (OSSOutput::Get()->OpenWrite()) | |||
| { | |||
| cb_Blocking(m_Parent,true); | |||
| m_Mode=OUTPUT; | |||
| } | |||
| } | |||
| //if (m_Mode==NO_MODE || m_Mode==CLOSED) cb_Blocking(m_Parent,false); | |||
| //else cb_Blocking(m_Parent,true); | |||
| if (m_Mode==OUTPUT || m_Mode==DUPLEX) | |||
| { | |||
| OSSOutput::Get()->SendStereo(GetInput(0),GetInput(1)); | |||
| @@ -177,6 +183,54 @@ void OutputPlugin::Execute() | |||
| if (m_Mode==INPUT || m_Mode==DUPLEX) OSSOutput::Get()->GetStereo(GetOutputBuf(0),GetOutputBuf(1)); | |||
| } | |||
| void OutputPlugin::ExecuteCommands() | |||
| { | |||
| // Only Play() once per set of plugins | |||
| m_NoExecuted++; | |||
| if (m_NoExecuted==m_RefCount) | |||
| { | |||
| if (m_Mode==INPUT || m_Mode==DUPLEX) OSSOutput::Get()->Read(); | |||
| if (m_Mode==OUTPUT || m_Mode==DUPLEX) OSSOutput::Get()->Play(); | |||
| m_NoExecuted=0; | |||
| } | |||
| if (m_AudioCH->IsCommandWaiting()) | |||
| { | |||
| switch(m_AudioCH->GetCommand()) | |||
| { | |||
| case OPENREAD : | |||
| if (OSSOutput::Get()->OpenRead()) | |||
| { | |||
| m_Mode=INPUT; | |||
| //cb_Blocking(m_Parent,true); | |||
| } | |||
| break; | |||
| case OPENWRITE : | |||
| if (OSSOutput::Get()->OpenWrite()) | |||
| { | |||
| m_Mode=OUTPUT; | |||
| cb_Blocking(m_Parent,true); | |||
| } | |||
| break; | |||
| case OPENDUPLEX : | |||
| if (OSSOutput::Get()->OpenReadWrite()) | |||
| { | |||
| m_Mode=DUPLEX; | |||
| cb_Blocking(m_Parent,true); | |||
| } | |||
| break; | |||
| case CLOSE : | |||
| m_Mode=CLOSED; | |||
| cb_Blocking(m_Parent,false); | |||
| OSSOutput::Get()->Close(); | |||
| break; | |||
| case SET_VOLUME : OSSOutput::Get()->SetVolume(m_Volume); break; | |||
| default : break; | |||
| } | |||
| } | |||
| } | |||
| ////////////////////////////////////////////////////////////////////// | |||
| ////////////////////////////////////////////////////////////////////// | |||
| @@ -185,14 +239,12 @@ m_Amp(0.5), | |||
| m_Channels(2), | |||
| m_ReadBufferNum(0), | |||
| m_WriteBufferNum(0), | |||
| m_OutputOk(true) | |||
| m_OutputOk(false) | |||
| { | |||
| m_Buffer[0]=NULL; | |||
| m_Buffer[1]=NULL; | |||
| m_InBuffer[0]=NULL; | |||
| m_InBuffer[1]=NULL; | |||
| OpenWrite(); | |||
| } | |||
| ////////////////////////////////////////////////////////////////////// | |||
| @@ -264,9 +316,10 @@ void OSSOutput::Play() | |||
| ((m_Buffer[BufferToSend][n]>>8)&0xff); | |||
| } | |||
| #endif | |||
| if (m_OutputOk) | |||
| { | |||
| write(m_Dspfd,m_Buffer[BufferToSend],m_BufSizeBytes); | |||
| } | |||
| if(m_Wav.Recording()) | |||
| { | |||
| @@ -316,15 +369,17 @@ void OSSOutput::Read() | |||
| ////////////////////////////////////////////////////////////////////// | |||
| void OSSOutput::Close() | |||
| bool OSSOutput::Close() | |||
| { | |||
| cerr<<"Closing dsp output"<<endl; | |||
| close(m_Dspfd); | |||
| return true; | |||
| } | |||
| ////////////////////////////////////////////////////////////////////// | |||
| void OSSOutput::OpenWrite() | |||
| bool OSSOutput::OpenWrite() | |||
| { | |||
| int result,val; | |||
| cerr<<"Opening dsp output"<<endl; | |||
| @@ -334,7 +389,7 @@ void OSSOutput::OpenWrite() | |||
| { | |||
| fprintf(stderr,"Can't open audio driver for writing.\n"); | |||
| m_OutputOk=false; | |||
| return; | |||
| return false; | |||
| } | |||
| result = ioctl(m_Dspfd,SNDCTL_DSP_RESET,NULL); | |||
| @@ -381,11 +436,14 @@ void OSSOutput::OpenWrite() | |||
| val = host->SAMPLERATE; | |||
| result = ioctl(m_Dspfd,SNDCTL_DSP_SPEED,&val); | |||
| CHECK_AND_REPORT_ERROR; | |||
| m_OutputOk=true; | |||
| return true; | |||
| } | |||
| ////////////////////////////////////////////////////////////////////// | |||
| void OSSOutput::OpenRead() | |||
| bool OSSOutput::OpenRead() | |||
| { | |||
| int result,val; | |||
| @@ -396,7 +454,7 @@ void OSSOutput::OpenRead() | |||
| { | |||
| fprintf(stderr,"Can't open audio driver for reading.\n"); | |||
| m_OutputOk=false; | |||
| return; | |||
| return false; | |||
| } | |||
| result = ioctl(m_Dspfd,SNDCTL_DSP_RESET,NULL); | |||
| @@ -413,11 +471,14 @@ void OSSOutput::OpenRead() | |||
| val = host->SAMPLERATE; | |||
| result = ioctl(m_Dspfd,SNDCTL_DSP_SPEED,&val); | |||
| CHECK_AND_REPORT_ERROR; | |||
| m_OutputOk=true; | |||
| return true; | |||
| } | |||
| ////////////////////////////////////////////////////////////////////// | |||
| void OSSOutput::OpenReadWrite() | |||
| bool OSSOutput::OpenReadWrite() | |||
| { | |||
| int result,val; | |||
| cerr<<"Opening dsp output (duplex)"<<endl; | |||
| @@ -427,7 +488,7 @@ void OSSOutput::OpenReadWrite() | |||
| { | |||
| fprintf(stderr,"Can't open audio driver for writing.\n"); | |||
| m_OutputOk=false; | |||
| return; | |||
| return false; | |||
| } | |||
| result = ioctl(m_Dspfd,SNDCTL_DSP_RESET,NULL); | |||
| @@ -474,4 +535,7 @@ void OSSOutput::OpenReadWrite() | |||
| val = host->SAMPLERATE; | |||
| result = ioctl(m_Dspfd,SNDCTL_DSP_SPEED,&val); | |||
| CHECK_AND_REPORT_ERROR; | |||
| m_OutputOk=true; | |||
| return true; | |||
| } | |||
| @@ -42,10 +42,10 @@ public: | |||
| void WavClose() {m_Wav.Close();} | |||
| short *GetBuffer() {return m_Buffer[m_WriteBufferNum];} | |||
| void OpenReadWrite(); | |||
| void OpenWrite(); | |||
| void OpenRead(); | |||
| void Close(); | |||
| bool OpenReadWrite(); | |||
| bool OpenWrite(); | |||
| bool OpenRead(); | |||
| bool Close(); | |||
| private: | |||
| static OSSOutput* m_Singleton; | |||
| @@ -68,7 +68,7 @@ private: | |||
| class OutputPlugin : public SpiralPlugin | |||
| { | |||
| public: | |||
| enum Mode{INPUT,OUTPUT,DUPLEX}; | |||
| enum Mode{NO_MODE,INPUT,OUTPUT,DUPLEX,CLOSED}; | |||
| OutputPlugin(); | |||
| virtual ~OutputPlugin(); | |||
| @@ -76,9 +76,13 @@ public: | |||
| virtual PluginInfo& Initialise(const HostInfo *Host); | |||
| virtual SpiralGUIType* CreateGUI(); | |||
| virtual void Execute(); | |||
| virtual void ExecuteCommands(); | |||
| virtual void StreamOut(ostream &s) {} | |||
| virtual void StreamIn(istream &s) {} | |||
| enum GUICommands {NONE,OPENREAD,OPENWRITE,OPENDUPLEX,CLOSE,SET_VOLUME}; | |||
| float m_Volume; | |||
| Mode GetMode() { return m_Mode; } | |||
| private: | |||
| @@ -71,7 +71,10 @@ void OutputPluginGUI::UpdateValues(SpiralPlugin *o) | |||
| //// Callbacks //// | |||
| inline void OutputPluginGUI::cb_Volume_i(Fl_Knob* o, void* v) | |||
| { OSSOutput::Get()->SetVolume(o->value()); } | |||
| { | |||
| m_GUICH->Set("Volume",(float)o->value()); | |||
| m_GUICH->SetCommand(OutputPlugin::SET_VOLUME); | |||
| } | |||
| void OutputPluginGUI::cb_Volume(Fl_Knob* o, void* v) | |||
| { ((OutputPluginGUI*)(o->parent()))->cb_Volume_i(o,v); } | |||
| @@ -104,14 +107,16 @@ inline void OutputPluginGUI::cb_OpenRead_i(Fl_Button* o, void* v) | |||
| if (o->value()) | |||
| { | |||
| OpenWrite->value(0); | |||
| OSSOutput::Get()->Close(); | |||
| OSSOutput::Get()->OpenRead(); | |||
| m_GUICH->Set("Mode",(char)OutputPlugin::INPUT); | |||
| m_GUICH->SetCommand(OutputPlugin::CLOSE); | |||
| m_GUICH->Wait(); | |||
| m_GUICH->SetCommand(OutputPlugin::OPENREAD); | |||
| m_GUICH->Wait(); | |||
| } | |||
| else | |||
| { | |||
| OpenWrite->value(0); | |||
| OSSOutput::Get()->Close(); | |||
| OpenWrite->value(0); | |||
| m_GUICH->SetCommand(OutputPlugin::CLOSE); | |||
| m_GUICH->Wait(); | |||
| } | |||
| } | |||
| void OutputPluginGUI::cb_OpenRead(Fl_Button* o, void* v) | |||
| @@ -122,14 +127,16 @@ inline void OutputPluginGUI::cb_OpenDuplex_i(Fl_Button* o, void* v) | |||
| if (o->value()) | |||
| { | |||
| OpenRead->value(0); | |||
| OSSOutput::Get()->Close(); | |||
| OSSOutput::Get()->OpenReadWrite(); | |||
| m_GUICH->Set("Mode",(char)OutputPlugin::DUPLEX); | |||
| m_GUICH->SetCommand(OutputPlugin::CLOSE); | |||
| m_GUICH->Wait(); | |||
| m_GUICH->SetCommand(OutputPlugin::OPENDUPLEX); | |||
| m_GUICH->Wait(); | |||
| } | |||
| else | |||
| { | |||
| OpenRead->value(0); | |||
| OSSOutput::Get()->Close(); | |||
| m_GUICH->SetCommand(OutputPlugin::CLOSE); | |||
| m_GUICH->Wait(); | |||
| } | |||
| } | |||
| void OutputPluginGUI::cb_OpenDuplex(Fl_Button* o, void* v) | |||
| @@ -140,14 +147,16 @@ inline void OutputPluginGUI::cb_OpenWrite_i(Fl_Button* o, void* v) | |||
| if (o->value()) | |||
| { | |||
| OpenRead->value(0); | |||
| OSSOutput::Get()->Close(); | |||
| OSSOutput::Get()->OpenWrite(); | |||
| m_GUICH->Set("Mode",(char)OutputPlugin::OUTPUT); | |||
| m_GUICH->SetCommand(OutputPlugin::CLOSE); | |||
| m_GUICH->Wait(); | |||
| m_GUICH->SetCommand(OutputPlugin::OPENWRITE); | |||
| m_GUICH->Wait(); | |||
| } | |||
| else | |||
| { | |||
| OpenRead->value(0); | |||
| OSSOutput::Get()->Close(); | |||
| m_GUICH->SetCommand(OutputPlugin::CLOSE); | |||
| m_GUICH->Wait(); | |||
| } | |||
| } | |||
| void OutputPluginGUI::cb_OpenWrite(Fl_Button* o, void* v) | |||
| @@ -94,11 +94,14 @@ public: | |||
| string GetName() { return m_PluginInfo.Name; } | |||
| void UpdatePluginInfoWithHost(); | |||
| void SetInPortType(PluginInfo &pinfo, int port, Sample::SampleType type); | |||
| void SetOutPortType(PluginInfo &pinfo, int port, Sample::SampleType type); | |||
| // Callbacks to main engine. Should only called by plugin hosts. | |||
| void SetUpdateInfoCallback(int ID, void(*s)(int, void *)); | |||
| void SetUpdateCallback(void (*s)(void*,bool m)) { cb_Update=s; } | |||
| void SetBlockingCallback(void (*s)(void*,bool m)) { cb_Blocking=s; } | |||
| void SetParent(void *s) { m_Parent=s; } | |||
| void SetInPortType(PluginInfo &pinfo, int port, Sample::SampleType type); | |||
| void SetOutPortType(PluginInfo &pinfo, int port, Sample::SampleType type); | |||
| void UpdateChannelHandler(); | |||
| @@ -146,6 +149,10 @@ protected: | |||
| // needed for jack | |||
| void (*cb_Update)(void*o ,bool m); | |||
| void *m_Parent; | |||
| // tell the engine that we are taking control of the | |||
| // timing for output. | |||
| void (*cb_Blocking)(void*o ,bool m); | |||
| private: | |||
| @@ -59,6 +59,7 @@ using namespace std; | |||
| map<int,DeviceWin*> SynthModular::m_DeviceWinMap; | |||
| bool SynthModular::m_CallbackUpdateMode = false; | |||
| bool SynthModular::m_BlockingOutputPluginIsReady = false; | |||
| ////////////////////////////////////////////////////////// | |||
| @@ -138,7 +139,7 @@ void SynthModular::Update() | |||
| if (i->second->m_Device) // if it's not a comment | |||
| { | |||
| #ifdef DEBUG_PLUGINS | |||
| cerr<<"Updating channelhandler of pluin "<<i->second->m_PluginID<<endl; | |||
| cerr<<"Updating channelhandler of plugin "<<i->second->m_PluginID<<endl; | |||
| #endif | |||
| // updates the data from the gui thread, if it's not blocking | |||
| @@ -191,21 +192,24 @@ void SynthModular::UpdatePluginGUIs() | |||
| if (i->second->m_DeviceGUI->Killed()) | |||
| { | |||
| if (i->second->m_Device) | |||
| { | |||
| PauseAudio(); | |||
| delete i->second->m_Device; | |||
| ResumeAudio(); | |||
| } | |||
| if (i->second->m_DeviceGUI->GetPluginWindow()) | |||
| { | |||
| i->second->m_DeviceGUI->GetPluginWindow()->hide(); | |||
| m_MainWindow->remove(i->second->m_DeviceGUI->GetPluginWindow()); | |||
| } | |||
| i->second->m_DeviceGUI->Clear(); | |||
| m_Canvas->RemoveDevice(i->second->m_DeviceGUI); | |||
| // deleted by Canvas::Remove()? seems to cause random crashes | |||
| //delete i->second->m_DeviceGUI; | |||
| if (i->second->m_Device) | |||
| { | |||
| PauseAudio(); | |||
| delete i->second->m_Device; | |||
| ResumeAudio(); | |||
| } | |||
| m_DeviceWinMap.erase(i); | |||
| break; | |||
| } | |||
| @@ -542,6 +546,7 @@ DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y) | |||
| { | |||
| return NULL; | |||
| } | |||
| nlw->m_Device->SetBlockingCallback(cb_Blocking); | |||
| nlw->m_Device->SetUpdateCallback(cb_Update); | |||
| nlw->m_Device->SetParent((void*)this); | |||
| @@ -662,12 +667,20 @@ void SynthModular::UpdateHostInfo() | |||
| ////////////////////////////////////////////////////////// | |||
| // called when a callback output plugin wants to run the audio thread | |||
| void SynthModular::cb_Update(void* o, bool mode) | |||
| { | |||
| m_CallbackUpdateMode=mode; | |||
| ((SynthModular*)o)->Update(); | |||
| } | |||
| // called by a blocking output plugin to notify the engine its ready to | |||
| // take control of the update timing (so take the brakes off) | |||
| void SynthModular::cb_Blocking(void* o, bool mode) | |||
| { | |||
| m_BlockingOutputPluginIsReady=mode; | |||
| } | |||
| ////////////////////////////////////////////////////////// | |||
| istream &operator>>(istream &s, SynthModular &o) | |||
| @@ -775,7 +788,6 @@ istream &operator>>(istream &s, SynthModular &o) | |||
| s>>*o.m_Canvas; | |||
| o.ResumeAudio(); | |||
| return s; | |||
| } | |||
| @@ -71,6 +71,7 @@ public: | |||
| void ClearUp(); | |||
| void UpdateHostInfo(); | |||
| bool CallbackMode() { return m_CallbackUpdateMode; } | |||
| bool IsBlockingOutputPluginReady() { return m_BlockingOutputPluginIsReady; } | |||
| void UpdatePluginGUIs(); | |||
| void LoadPatch(const char *fn); | |||
| @@ -85,6 +86,9 @@ public: | |||
| m_CH.Set("PauseAudio",false); | |||
| } | |||
| // only for audio thread | |||
| bool IsPaused() { return m_PauseAudio; } | |||
| private: | |||
| DeviceWin* NewDeviceWin(int n, int x, int y); | |||
| @@ -104,6 +108,7 @@ private: | |||
| int m_NextPluginButtonYPos; | |||
| static bool m_CallbackUpdateMode; | |||
| static bool m_BlockingOutputPluginIsReady; | |||
| string m_FilePath; | |||
| // Main GUI stuff | |||
| @@ -164,8 +169,10 @@ private: | |||
| static void cb_Rload(Fl_Button* o, void* v); | |||
| static void cb_Update(void* o, bool Mode); | |||
| static void cb_Blocking(void* o, bool Mode); | |||
| static void cb_UpdatePluginInfo(int ID, void *PluginInfo); | |||
| friend istream &operator>>(istream &s, SynthModular &o); | |||
| friend ostream &operator<<(ostream &s, SynthModular &o); | |||
| }; | |||
| @@ -25,30 +25,66 @@ | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Tooltip.h> | |||
| #include <unistd.h> | |||
| #include <sys/time.h> | |||
| #include <sys/resource.h> | |||
| #include "SpiralSynthModular.h" | |||
| pthread_t loopthread; | |||
| pthread_t loopthread,watchdogthread; | |||
| SynthModular *synth; | |||
| char watchdog_check = 1; | |||
| char gui_watchdog_check = 1; | |||
| int pthread_create_realtime (pthread_t *new_thread, | |||
| void *(*start)(void *), void *arg, | |||
| int priority); | |||
| bool CallbackOnly = false; | |||
| bool FIFO = false; | |||
| bool GUI = true; | |||
| ///////////////////////////////////////////////////////////// | |||
| void watchdog (void *arg) | |||
| { | |||
| pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); | |||
| watchdog_check = 0; | |||
| gui_watchdog_check = 0; | |||
| while (1) | |||
| { | |||
| usleep (10000000); | |||
| if (watchdog_check == 0 || gui_watchdog_check== 0) | |||
| { | |||
| cerr<<"ssm watchdog: timeout - killing ssm"<<endl; | |||
| if (watchdog_check==0) cerr<<"diagnosis: audio hung?"<<endl; | |||
| if (gui_watchdog_check==0) cerr<<"diagnosis: gui starved by audio"<<endl; | |||
| exit (1); | |||
| } | |||
| watchdog_check = 0; | |||
| gui_watchdog_check = 0; | |||
| } | |||
| } | |||
| /////////////////////////////////////////////////////////////////////// | |||
| void audioloop(void* o) | |||
| { | |||
| while(1) | |||
| { | |||
| { | |||
| if (!synth->CallbackMode()) | |||
| { | |||
| // do funky stuff | |||
| synth->Update(); | |||
| // slow down this thread if we are not going to be using the | |||
| // oss plugin. prevents maxing the CPU out for no reason. | |||
| if (CallbackOnly) usleep(100); | |||
| // put the brakes on if there is no blocking output running | |||
| if (!synth->IsBlockingOutputPluginReady()|| | |||
| synth->IsPaused()) | |||
| { | |||
| usleep(10000); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| @@ -56,17 +92,18 @@ void audioloop(void* o) | |||
| // need to do anything unless we are switched back | |||
| usleep(1000000); | |||
| } | |||
| watchdog_check = 1; | |||
| } | |||
| } | |||
| ////////////////////////////////////////////////////// | |||
| int main(int argc, char **argv) | |||
| { | |||
| srand(time(NULL)); | |||
| SpiralSynthModularInfo::Get()->LoadPrefs(); | |||
| bool GUI = true; | |||
| bool FIFO = false; | |||
| // get args | |||
| string cmd_filename=""; | |||
| bool cmd_specd = false; | |||
| @@ -78,24 +115,22 @@ int main(int argc, char **argv) | |||
| for (int a=1; a<argc; a++) | |||
| { | |||
| if (!strcmp(argv[a],"--NoGUI")) GUI = false; | |||
| else if (!strcmp(argv[a],"--SHED_FIFO")) FIFO = true; | |||
| else if (!strcmp(argv[a],"--CallbackOnly")) CallbackOnly = true; | |||
| else if (!strcmp(argv[a],"--Realtime")) FIFO = true; | |||
| else if (!strcmp(argv[a],"-h")) | |||
| { | |||
| cout<<"usage: spiralsynthmodular [options] [patch.ssm]"<<endl<<endl | |||
| cout<<"usage: spiralsynthmodular [patch.ssm] [options]"<<endl<<endl | |||
| <<"options list"<<endl | |||
| <<"-h : help"<<endl | |||
| <<"-v : print version"<<endl | |||
| <<"--NoGUI : run without GUI (only useful when loading patch from command line"<<endl | |||
| <<"--SHED_FIFO : spawn audio thread with FIFO sheduling (run as root)"<<endl | |||
| <<"--CallbackOnly : prevents 100% cpu usage when idle, but makes OSS output unsable"<<endl | |||
| <<"--Realtime : spawn audio thread with FIFO scheduling (run as root)"<<endl | |||
| <<"--PluginPath <PATH> : look for plugins in the specified directory"<<endl; | |||
| exit(0); | |||
| } | |||
| else if (!strcmp(argv[a],"-v")) | |||
| { | |||
| cout<<VER_STRING<<endl; exit(0); | |||
| } | |||
| } | |||
| else if (!strcmp(argv[a],"--PluginPath")) | |||
| { | |||
| a++; | |||
| @@ -108,12 +143,10 @@ int main(int argc, char **argv) | |||
| } | |||
| } | |||
| } | |||
| // set some fltk defaults | |||
| Fl_Tooltip::size(10); | |||
| Fl::visible_focus(false); | |||
| //Fl::set_font(FL_HELVETICA,FL_SCREEN); | |||
| Fl::visual(FL_DOUBLE|FL_RGB); | |||
| synth=new SynthModular; | |||
| @@ -125,11 +158,17 @@ int main(int argc, char **argv) | |||
| if (GUI) win->show(1, argv); // prevents stuff happening before the plugins have loaded | |||
| // spawn the audio thread | |||
| int ret; | |||
| if (FIFO) ret=pthread_create_realtime(&loopthread,(void*(*)(void*))audioloop,NULL,10); | |||
| else ret=pthread_create(&loopthread,NULL,(void*(*)(void*))audioloop,NULL); | |||
| pthread_t GUIThread = pthread_self(); | |||
| if (FIFO) | |||
| { | |||
| pthread_create_realtime(&watchdogthread,(void*(*)(void*))watchdog,NULL,sched_get_priority_max(SCHED_FIFO)); | |||
| pthread_create_realtime(&loopthread,(void*(*)(void*))audioloop,NULL,sched_get_priority_max(SCHED_FIFO)-1); | |||
| } | |||
| else | |||
| { | |||
| pthread_create(&loopthread,NULL,(void*(*)(void*))audioloop,NULL); | |||
| // reduce the priority of the gui | |||
| if (setpriority(PRIO_PROCESS,0,20)) cerr<<"Could not set priority for GUI thread"<<endl; | |||
| } | |||
| // do we need to load a patch on startup? | |||
| if (cmd_specd) synth->LoadPatch(cmd_filename.c_str()); | |||
| @@ -146,6 +185,7 @@ int main(int argc, char **argv) | |||
| if (!Fl::check()) break; | |||
| synth->UpdatePluginGUIs(); // deletes any if necc | |||
| usleep(10000); | |||
| gui_watchdog_check=1; | |||
| } | |||
| //pthread_cancel(loopthread); | |||