/[svn]/linuxsampler/trunk/src/hostplugins/vst/PluginVst.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/hostplugins/vst/PluginVst.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1897 by persson, Sun May 10 09:31:51 2009 UTC revision 3289 by schoenebeck, Fri Jun 23 12:06:00 2017 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2008 Andreas Persson                                    *   *   Copyright (C) 2008 - 2013 Andreas Persson                             *
4   *                                                                         *   *                                                                         *
5   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
6   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 24  Line 24 
24    
25  #ifdef WIN32  #ifdef WIN32
26  #include <tchar.h>  #include <tchar.h>
27    #else
28    #include <cerrno>
29    #include <signal.h>
30    #include <sys/wait.h>
31    #include <unistd.h>
32    #include <dirent.h>
33    #include <sys/stat.h>
34  #endif  #endif
35    
36  #include "PluginVst.h"  #include "PluginVst.h"
37    #include "../../drivers/midi/MidiInstrumentMapper.h"
38    
39    #ifndef CHANNELS
40    #define CHANNELS 32
41    #endif
42    
43    // the sampler can actually hold up a very huge amount of programs
44    // (2^32 [maps] * 2^16 [banks MSB + LSB] * 2^8 [programs per bank]  =  2^56 [total programs] )
45    // however I am not sure if we might crash some VST host with a number
46    // here that is too huge, however this should be enough in most cases ...
47    #define MAX_VST_PROGRAMS (128*128)
48    
49    using namespace LinuxSampler;
50    
51  namespace {  namespace {
52    
# Line 38  namespace { Line 58  namespace {
58      LinuxSampler::Mutex logmutex;      LinuxSampler::Mutex logmutex;
59    
60      void log(const char* fmt, ...) {      void log(const char* fmt, ...) {
61          logmutex.Lock();          LockGuard lock(logmutex);
62          FILE* f = fopen("C:\\linuxsamplervst.log", "a");          FILE* f = fopen((String(getenv("TEMP")) + "\\linuxsamplervst.log").c_str(), "a");
63          va_list ap;          va_list ap;
64          va_start(ap, fmt);          va_start(ap, fmt);
65          vfprintf(f, fmt, ap);          vfprintf(f, fmt, ap);
66          va_end(ap);          va_end(ap);
67          fclose(f);          fclose(f);
         logmutex.Unlock();  
68      }      }
69  #undef dmsg  #undef dmsg
70  #define dmsg(debuglevel,x) log x;  #define dmsg(debuglevel,x) log x;
71  #endif  #endif
72    
73    
 // *************** LinuxSamplerVstProgram ***************  
 // *  
   
     LinuxSamplerVstProgram::LinuxSamplerVstProgram() {  
         vst_strncpy(name, "Basic", kVstMaxProgNameLen);  
     }  
   
   
74  // *************** LinuxSamplerEditor ***************  // *************** LinuxSamplerEditor ***************
75  // *  // *
76    
# Line 68  namespace { Line 79  namespace {
79          dmsg(2, ("-->LinuxSamplerEditor constructor\n"));          dmsg(2, ("-->LinuxSamplerEditor constructor\n"));
80  #ifdef WIN32  #ifdef WIN32
81          ProcessHandle = INVALID_HANDLE_VALUE;          ProcessHandle = INVALID_HANDLE_VALUE;
82    #else
83            pid = 0;
84  #endif  #endif
85      }      }
86    
# Line 75  namespace { Line 88  namespace {
88          close();          close();
89      }      }
90    
91      bool LinuxSamplerEditor::open(void* ptr) {      String LinuxSamplerEditor::FindFantasia() {
92          dmsg(2, ("-->LinuxSamplerEditor::open\n"));          String fantasia;
         AEffEditor::open(ptr);  
   
93  #ifdef WIN32  #ifdef WIN32
94          // try to start the JSample Fantasia GUI as a separate process          // assume Fantasia is in the same directory or one directory above
95            // the liblinuxsampler dll
         ProcessHandle = INVALID_HANDLE_VALUE;  
   
         // assume Fantasia is in the same directory as the  
         // liblinuxsampler dll  
96          String lspath = LinuxSampler::Sampler::GetInstallDir();          String lspath = LinuxSampler::Sampler::GetInstallDir();
97          if (!lspath.empty()) {          if (!lspath.empty()) {
98              lspath += "\\";              lspath += "\\";
99              WIN32_FIND_DATA fd;              WIN32_FIND_DATA fd;
100              HANDLE hFind = FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd);              HANDLE hFind =
101                    FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd);
102                if (hFind == INVALID_HANDLE_VALUE) {
103                    lspath += "..\\";
104                    hFind = FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd);
105                }
106              if (hFind != INVALID_HANDLE_VALUE) {              if (hFind != INVALID_HANDLE_VALUE) {
107                  String fantasia(fd.cFileName);                  fantasia = fd.cFileName;
108                  FindClose(hFind);                  FindClose(hFind);
109                    return lspath + fantasia;
110                }
111            }
112    #elif defined(__APPLE__)
113            // look for the java application wrapper
114            const char* cmd =
115                "/Applications/LinuxSampler/Fantasia.app"
116                "/Contents/MacOS/JavaApplicationStub";
117            fantasia = String(getenv("HOME")) + cmd;
118            struct stat buf;
119            if (stat(fantasia.c_str(), &buf) != 0) {
120                fantasia = stat(cmd, &buf) == 0 ? cmd : "";
121            }
122    #else
123            // search in <datadir>/java (default: /usr/local/share/java)
124            String path(DATADIR);
125            path += *(path.end() - 1) == '/' ? "java" : "/java";
126            DIR* dir = opendir(path.c_str());
127            if (dir) {
128                while (dirent* d = readdir(dir)) {
129                    const char* name = d->d_name;
130                    if (strncmp("Fantasia", name, 8) == 0 &&
131                        strcmp(name + strlen(name) - 4, ".jar") == 0) {
132                        fantasia = path + "/" + name;
133                    }
134                }
135                closedir(dir);
136            }
137    #endif
138            return fantasia;
139        }
140    
141        bool LinuxSamplerEditor::open(void* ptr) {
142            dmsg(2, ("-->LinuxSamplerEditor::open\n"));
143            AEffEditor::open(ptr);
144    
145                  // start a java process          // try to start the JSampler Fantasia GUI as a separate process
146    #ifdef WIN32
147            // first check if it's already running
148            if (ProcessHandle != INVALID_HANDLE_VALUE) {
149                DWORD exitCode;
150                if (GetExitCodeProcess(ProcessHandle, &exitCode)) {
151                    if (exitCode == STILL_ACTIVE) return true;
152                }
153                free(Command);
154                CloseHandle(ProcessHandle);
155                ProcessHandle = INVALID_HANDLE_VALUE;
156            }
157    
158            String fantasia = FindFantasia();
159            if (!fantasia.empty()) {
160                // start a java process
161                String path; // look in PATH first
162                for (int i = 0 ; i < 2 ; i++) { // two tries
163                  STARTUPINFO si;                  STARTUPINFO si;
164                  PROCESS_INFORMATION pi;                  PROCESS_INFORMATION pi;
165                  ZeroMemory(&si, sizeof(si));                  ZeroMemory(&si, sizeof(si));
166                  si.cb = sizeof(si);                  si.cb = sizeof(si);
167                  ZeroMemory(&pi, sizeof(pi));                  ZeroMemory(&pi, sizeof(pi));
168    
169                  Command = _tcsdup(TEXT((String("javaw -jar \"") + lspath + fantasia + "\" &").c_str()));                  Command = _tcsdup(TEXT((String("\"") + path +
170                  CreateProcess(NULL, Command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);                                          "javaw\" -jar \"" + fantasia +
171                  ProcessHandle = pi.hProcess;                                          "\"").c_str()));
172                    if (CreateProcess(NULL, Command, NULL, NULL, FALSE, 0, NULL,
173                                      NULL, &si, &pi)) {
174                        ProcessHandle = pi.hProcess;
175                        CloseHandle(pi.hThread);
176                        break;
177                    } else {
178                        free(Command);
179                        if (path.empty()) {
180                            // java wasn't found in PATH, try again
181                            // with an alternative directory
182                            char* windir = getenv("windir");
183                            if (!windir) break;
184    #ifdef _WIN64
185                            // LS plugin is 64 bit - look for 32 bit java
186                            path = String(windir) + "\\SysWOW64\\";
187    #else
188                            // LS plugin is 32 bit - look for 64 bit java
189                            path = String(windir) + "\\Sysnative\\";
190    #endif
191                        }
192                    }
193                }
194            }
195    #else
196            // first check if it's already running
197            if (pid && waitpid(pid, 0, WNOHANG) == 0) return true;
198    
199            String fantasia = FindFantasia();
200            if (!fantasia.empty())
201            {
202                // start a java process
203                pid = fork();
204                if (pid == -1) {
205                    dmsg(1, ("fork failed %d %s\n", errno, strerror(errno)));
206                    pid = 0;
207                } else if (pid == 0) {
208    #ifdef __APPLE__
209                    execl(fantasia.c_str(), fantasia.c_str(), (char*)0);
210    #else
211                    execlp("java", "java", "-jar", fantasia.c_str(), (char*)0);
212    #endif
213                    dmsg(1, ("exec failed %d %s\n", errno, strerror(errno)));
214    
215                    // make sure something is executed, so the static
216                    // destructors copied from the parent don't run
217                    execl("/usr/bin/true", "/usr/bin/true", (char*)0);
218              }              }
219          }          }
220  #endif  #endif
# Line 118  namespace { Line 228  namespace {
228          if (ProcessHandle != INVALID_HANDLE_VALUE) {          if (ProcessHandle != INVALID_HANDLE_VALUE) {
229              TerminateProcess(ProcessHandle, 0);              TerminateProcess(ProcessHandle, 0);
230              free(Command);              free(Command);
231                CloseHandle(ProcessHandle);
232              ProcessHandle = INVALID_HANDLE_VALUE;              ProcessHandle = INVALID_HANDLE_VALUE;
233          }          }
234    #else
235            if (pid) {
236                kill(pid, SIGTERM);
237                waitpid(pid, 0, 0);
238                pid = 0;
239            }
240  #endif  #endif
241      }      }
242    
# Line 127  namespace { Line 244  namespace {
244          ERect* r = new ERect();          ERect* r = new ERect();
245          r->top = 0;          r->top = 0;
246          r->left = 0;          r->left = 0;
247          r->bottom = 0;          r->bottom = 1; // 0 makes EnergyXT and Ardour on Linux crash
248          r->right = 400;          r->right = 400;
249          *rect = r;          *rect = r;
250          return true;          return true;
# Line 138  namespace { Line 255  namespace {
255  // *  // *
256    
257      LinuxSamplerVst::LinuxSamplerVst(audioMasterCallback audioMaster) :      LinuxSamplerVst::LinuxSamplerVst(audioMasterCallback audioMaster) :
258          AudioEffectX(audioMaster, NbPrograms, 0),          AudioEffectX(audioMaster, MAX_VST_PROGRAMS, 0),
259          StateBuf(0)          StateBuf(0)
260      {      {
261          dmsg(2, ("-->constructor\n"));          dmsg(2, ("-->constructor\n"));
262    
         Programs = new LinuxSamplerVstProgram[NbPrograms];  
263          setProgram(0);          setProgram(0);
264          setNumInputs(0);          setNumInputs(0);
265          setNumOutputs(2);          setNumOutputs(CHANNELS);
266          canProcessReplacing();          canProcessReplacing();
267          isSynth();          isSynth();
268          programsAreChunks();          programsAreChunks();
# Line 160  namespace { Line 276  namespace {
276    
277      void LinuxSamplerVst::resume() {      void LinuxSamplerVst::resume() {
278          dmsg(2, ("-->resume\n"));          dmsg(2, ("-->resume\n"));
         if (!pAudioDevice) {  
             Init(int(sampleRate), blockSize);  
279    
280              if (!SavedChunk.empty()) {          // Ardour initially sets blockSize to zero - we postpone the
281                  SetState(SavedChunk);          // initialization until we have a blockSize
282                  SavedChunk.clear();          if (blockSize != 0) {
283                if (!pAudioDevice) {
284                    Init(int(sampleRate), blockSize, CHANNELS);
285    
286                    if (!SavedChunk.empty()) {
287                        SetState(SavedChunk);
288                        SavedChunk.clear();
289                    } else {
290                        InitState();
291                    }
292              } else {              } else {
293                  InitState();                  Init(int(sampleRate), blockSize, CHANNELS);
294              }              }
         } else {  
             Init(int(sampleRate), blockSize);  
295          }          }
296            AudioEffectX::resume();
297          dmsg(2, ("<--resume\n"));          dmsg(2, ("<--resume\n"));
298      }      }
299    
300      LinuxSamplerVst::~LinuxSamplerVst() {      LinuxSamplerVst::~LinuxSamplerVst() {
301          dmsg(2, ("-->destructor\n"));          dmsg(2, ("-->destructor\n"));
         delete[] Programs;  
302          if (StateBuf) free(StateBuf);          if (StateBuf) free(StateBuf);
303          dmsg(2, ("<--destructor\n"));          dmsg(2, ("<--destructor\n"));
304      }      }
305    
306    
307      void LinuxSamplerVst::setProgram(VstInt32 program) {      void LinuxSamplerVst::setProgram(VstInt32 program) {
308          if (program < 0 || program >= NbPrograms) return;          if (program < 0 || program >= MAX_VST_PROGRAMS ||
309                !pMidiDevice || !pMidiDevice->Port()) return;
310          curProgram = program;          
311            int i = 0;
312            const std::vector<int> maps = MidiInstrumentMapper::Maps();
313            for (int iMap = 0; iMap < maps.size(); ++iMap) {
314                const int mapID = maps[iMap];
315                const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings =
316                    MidiInstrumentMapper::Entries(maps[mapID]);
317                for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin();
318                     iter != mappings.end(); ++iter, ++i)
319                {
320                    if (i == program) {
321                        //TODO: switch MIDI instrument map before sending bank select and program change
322                        
323                        const uint iMIDIChannel = 0;
324                        pMidiDevice->Port()->DispatchBankSelectMsb(iter->first.midi_bank_msb, iMIDIChannel);
325                        pMidiDevice->Port()->DispatchBankSelectLsb(iter->first.midi_bank_lsb, iMIDIChannel);
326                        pMidiDevice->Port()->DispatchProgramChange(iter->first.midi_prog, iMIDIChannel);
327                        
328                        curProgram = program;
329                        
330                        return;
331                    }
332                }
333            }
334      }      }
335    
   
336      void LinuxSamplerVst::setProgramName(char* name) {      void LinuxSamplerVst::setProgramName(char* name) {
337          vst_strncpy(Programs[curProgram].name, name, kVstMaxProgNameLen);          int i = 0;
338            const std::vector<int> maps = MidiInstrumentMapper::Maps();
339            for (int iMap = 0; iMap < maps.size(); ++iMap) {
340                const int mapID = maps[iMap];
341                const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings =
342                    MidiInstrumentMapper::Entries(maps[mapID]);
343                for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin();
344                     iter != mappings.end(); ++iter, ++i)
345                {
346                    if (i == curProgram) {
347                        char buf[kVstMaxProgNameLen + 1] = {};
348                        vst_strncpy(buf, name, kVstMaxProgNameLen);
349                        MidiInstrumentMapper::entry_t entry = iter->second;
350                        entry.Name = buf;
351                        try {
352                            MidiInstrumentMapper::AddOrReplaceEntry(
353                                mapID, iter->first, entry, false/*bInBackground*/
354                            );
355                        } catch (Exception e) {
356                            e.PrintMessage();
357                        }
358                        return;
359                    }
360                }
361            }
362      }      }
363    
   
364      void LinuxSamplerVst::getProgramName(char* name) {      void LinuxSamplerVst::getProgramName(char* name) {
365          vst_strncpy(name, Programs[curProgram].name, kVstMaxProgNameLen);          if (!getProgramNameIndexed(0 /*dont care*/, curProgram, name)) {
366                vst_strncpy(name, "unknown", kVstMaxProgNameLen);
367            }
368      }      }
369    
370        bool LinuxSamplerVst::getProgramNameIndexed(VstInt32 /*category*/, VstInt32 index,
371      bool LinuxSamplerVst::getOutputProperties(VstInt32 index,                                                  char* text)
372                                                VstPinProperties* properties) {      {
373          if (index < 2) {          //NOTE: parameter 'category' is unused in VST 2.4
374              sprintf(properties->label, "LS %d", index + 1);          
375              properties->flags = kVstPinIsActive | kVstPinIsStereo;          int i = 0;
376              return true;          const std::vector<int> maps = MidiInstrumentMapper::Maps();
377            for (int iMap = 0; iMap < maps.size(); ++iMap) {
378                const int mapID = maps[iMap];
379                const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings =
380                    MidiInstrumentMapper::Entries(maps[mapID]);
381                for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin();
382                     iter != mappings.end(); ++iter, ++i)
383                {
384                    if (i == index) {
385                        vst_strncpy(text, iter->second.Name.c_str(), kVstMaxProgNameLen);
386                        return true;
387                    }
388                }
389          }          }
390          return false;          return false;
391      }      }
392    
393    
394      bool LinuxSamplerVst::getProgramNameIndexed(VstInt32 category, VstInt32 index,      bool LinuxSamplerVst::getOutputProperties(VstInt32 index,
395                                                  char* text) {                                                VstPinProperties* properties) {
396          if (index < NbPrograms) {          if (index < CHANNELS) {
397              vst_strncpy(text, Programs[index].name, kVstMaxProgNameLen);              sprintf(properties->label, "LS %d", index + 1);
398                properties->flags = kVstPinIsActive | kVstPinIsStereo;
399              return true;              return true;
400          }          }
401          return false;          return false;
# Line 246  namespace { Line 427  namespace {
427    
428      VstInt32 LinuxSamplerVst::canDo(char* text) {      VstInt32 LinuxSamplerVst::canDo(char* text) {
429          dmsg(2, ("canDo %s\n", text));          dmsg(2, ("canDo %s\n", text));
430            
431            // supported features
432          if (strcmp(text, "receiveVstEvents") == 0 ||          if (strcmp(text, "receiveVstEvents") == 0 ||
433              strcmp(text, "receiveVstMidiEvent") == 0) return 1;              strcmp(text, "receiveVstMidiEvent") == 0 ||
434          return -1;              strcmp(text, "midiProgramNames") == 0) return 1;
435            
436            // not supported features
437            if (strcmp(text, "sendVstEvents") == 0 ||
438                strcmp(text, "sendVstMidiEvent") == 0 ||
439                strcmp(text, "receiveVstTimeInfo") == 0 ||
440                strcmp(text, "offline") == 0 ||
441                strcmp(text, "bypass") == 0) return -1;
442            
443            return 0; // "don't know", never heard of this feature
444      }      }
445    
446      void LinuxSamplerVst::setSampleRate(float sampleRate) {      void LinuxSamplerVst::setSampleRate(float sampleRate) {
# Line 265  namespace { Line 457  namespace {
457      void LinuxSamplerVst::processReplacing(float** inputs, float** outputs,      void LinuxSamplerVst::processReplacing(float** inputs, float** outputs,
458                                             VstInt32 sampleFrames) {                                             VstInt32 sampleFrames) {
459          if (pAudioDevice) {          if (pAudioDevice) {
460              pAudioDevice->Channel(0)->SetBuffer(outputs[0]);              for (int i = 0 ; i < CHANNELS ; i++) {
461              pAudioDevice->Channel(1)->SetBuffer(outputs[1]);                  pAudioDevice->Channel(i)->SetBuffer(outputs[i]);
462                }
463              pAudioDevice->Render(sampleFrames);              pAudioDevice->Render(sampleFrames);
464          } else {          } else {
465              memset(outputs[0], 0, sampleFrames * sizeof(float));              for (int i = 0 ; i < CHANNELS ; i++) {
466              memset(outputs[1], 0, sampleFrames * sizeof(float));                  memset(outputs[i], 0, sampleFrames * sizeof(float));
467                }
468          }          }
469      }      }
470    
# Line 299  namespace { Line 493  namespace {
493          StateBuf = strdup(state.c_str());          StateBuf = strdup(state.c_str());
494          *data = StateBuf;          *data = StateBuf;
495          dmsg(2, ("<--getChunk\n"));          dmsg(2, ("<--getChunk\n"));
496          return state.length() + 1;          return (VstInt32) state.length() + 1;
497      }      }
498    
499    

Legend:
Removed from v.1897  
changed lines
  Added in v.3289

  ViewVC Help
Powered by ViewVC