/[svn]/linuxsampler/trunk/src/engines/InstrumentManagerThread.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/InstrumentManagerThread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4019 - (hide annotations) (download)
Mon Jan 3 17:45:26 2022 UTC (2 years, 3 months ago) by schoenebeck
File size: 11471 byte(s)
* SFZ engine: Automatically reload .sfz files on file changes
  (e.g. by some external text editor).

* Bumped version (2.2.0.svn14).

1 schoenebeck 947 /***************************************************************************
2     * *
3 schoenebeck 4019 * Copyright (C) 2005 - 2022 Christian Schoenebeck *
4 schoenebeck 947 * *
5     * This library is free software; you can redistribute it and/or modify *
6     * it under the terms of the GNU General Public License as published by *
7     * the Free Software Foundation; either version 2 of the License, or *
8     * (at your option) any later version. *
9     * *
10     * This library is distributed in the hope that it will be useful, *
11     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13     * GNU General Public License for more details. *
14     * *
15     * You should have received a copy of the GNU General Public License *
16     * along with this library; if not, write to the Free Software *
17     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
18     * MA 02111-1307 USA *
19     ***************************************************************************/
20    
21     #include "InstrumentManagerThread.h"
22    
23 schoenebeck 1424 #include "../common/global_private.h"
24 iliev 1826 #include "EngineChannelFactory.h"
25 schoenebeck 1424
26 schoenebeck 4019 // how often periodic jobs for external components shall be executed
27     // (every 200ms)
28     #define PERIODIC_JOBS_SEC 0
29     #define PERIODIC_JOBS_NSEC (200 * 1000 * 1000)
30    
31 schoenebeck 947 namespace LinuxSampler {
32    
33     InstrumentManagerThread::InstrumentManagerThread() : Thread(true, false, 0, -4) {
34 iliev 1761 eventHandler.pThread = this;
35 schoenebeck 947 }
36    
37     InstrumentManagerThread::~InstrumentManagerThread() {
38 schoenebeck 3290 Thread::StopThread();
39 schoenebeck 947 }
40    
41     /**
42     * @brief Order loading of a new instrument.
43     *
44     * The request will go into a queue waiting to be processed by the
45     * class internal task thread. This method will immediately return and
46     * the instrument will be loaded in the background.
47     *
48     * @param Filename - file name of the instrument
49     * @param uiInstrumentIndex - index of the instrument within the file
50     * @param pEngineChannel - engine channel on which the instrument should be loaded
51     */
52     void InstrumentManagerThread::StartNewLoad(String Filename, uint uiInstrumentIndex, EngineChannel* pEngineChannel) {
53 persson 1038 dmsg(1,("Scheduling '%s' (Index=%d) to be loaded in background (if not loaded yet).\n",Filename.c_str(),uiInstrumentIndex));
54 iliev 1761
55 persson 2324 // the listener only needs to be registered once in the
56     // Sampler, but as we don't know if Sampler has been
57     // recreated, we simply remove and add every time
58     pEngineChannel->GetSampler()->RemoveChannelCountListener(&eventHandler);
59     pEngineChannel->GetSampler()->AddChannelCountListener(&eventHandler);
60 iliev 1761
61 schoenebeck 947 command_t cmd;
62     cmd.type = command_t::DIRECT_LOAD;
63     cmd.pEngineChannel = pEngineChannel;
64 persson 2277 cmd.instrumentId.Index = uiInstrumentIndex;
65     cmd.instrumentId.FileName = Filename;
66 schoenebeck 989
67 persson 2427 {
68     LockGuard lock(mutex);
69     queue.push_back(cmd);
70     }
71 schoenebeck 989
72 schoenebeck 947 StartThread(); // ensure thread is running
73     conditionJobsLeft.Set(true); // wake up thread
74     }
75    
76     /**
77     * @brief Order changing the life-time strategy of an instrument.
78     *
79     * The request will go into a queue waiting to be processed by the
80     * class internal task thread. This method will immediately return and
81     * in case the instrument has to be loaded due to a mode change to
82     * PERSISTENT, it will be loaded in the background.
83     *
84     * @param pManager - InstrumentManager which manages the instrument
85     * @param ID - unique ID of the instrument
86     * @param Mode - life-time strategy to set for this instrument
87     */
88     void InstrumentManagerThread::StartSettingMode(InstrumentManager* pManager, const InstrumentManager::instrument_id_t& ID, InstrumentManager::mode_t Mode) {
89     command_t cmd;
90     cmd.type = command_t::INSTR_MODE;
91     cmd.pManager = pManager;
92     cmd.instrumentId = ID;
93     cmd.mode = Mode;
94 schoenebeck 989
95 persson 2427 {
96     LockGuard lock(mutex);
97     queue.push_back(cmd);
98     }
99 schoenebeck 989
100 schoenebeck 947 StartThread(); // ensure thread is running
101     conditionJobsLeft.Set(true); // wake up thread
102     }
103    
104     // Entry point for the task thread.
105     int InstrumentManagerThread::Main() {
106 schoenebeck 3766 #if DEBUG
107     Thread::setNameOfCaller("InstrumentMngr");
108     #endif
109    
110 schoenebeck 947 while (true) {
111 nagata 1649
112 persson 2185 TestCancel();
113 nagata 1649
114 schoenebeck 3766 // prevent thread from being cancelled
115     // (e.g. to prevent deadlocks while holding mutex lock(s))
116     pushCancelable(false);
117    
118 persson 2324 while (true) {
119 schoenebeck 947 command_t cmd;
120 schoenebeck 989
121     // grab a new command from the queue
122 persson 2427 {
123     LockGuard lock(mutex);
124     if (queue.empty()) break;
125    
126 persson 2324 cmd = queue.front();
127     queue.pop_front();
128 persson 2326
129     if (cmd.type == command_t::DIRECT_LOAD) {
130     EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, false);
131     }
132 persson 2324 }
133 schoenebeck 989
134 schoenebeck 947 try {
135     switch (cmd.type) {
136     case command_t::DIRECT_LOAD:
137 persson 2277 cmd.pEngineChannel->PrepareLoadInstrument(cmd.instrumentId.FileName.c_str(), cmd.instrumentId.Index);
138 schoenebeck 947 cmd.pEngineChannel->LoadInstrument();
139 iliev 1826 EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, true);
140 schoenebeck 947 break;
141     case command_t::INSTR_MODE:
142     cmd.pManager->SetMode(cmd.instrumentId, cmd.mode);
143     break;
144     default:
145     std::cerr << "InstrumentManagerThread: unknown command - BUG!\n" << std::flush;
146     }
147 schoenebeck 1040 } catch (Exception e) {
148 schoenebeck 947 e.PrintMessage();
149 iliev 1826 if (cmd.type == command_t::DIRECT_LOAD) {
150     EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, true);
151     }
152 schoenebeck 1040 } catch (...) {
153     std::cerr << "InstrumentManagerThread: some exception occured, could not finish task\n" << std::flush;
154 iliev 1826 if (cmd.type == command_t::DIRECT_LOAD) {
155     EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, true);
156     }
157 schoenebeck 947 }
158     }
159    
160 schoenebeck 4019 // perform periodic, custom jobs on behalf of external components
161     {
162     LockGuard lock(periodicJobsMutex);
163     for (ext_job_t job : periodicJobs) {
164     job.fn();
165     }
166     }
167    
168 schoenebeck 3766 // now allow thread being cancelled again
169     // (since all mutexes are now unlocked)
170     popCancelable();
171    
172 schoenebeck 947 // nothing left to do, sleep until new jobs arrive
173 schoenebeck 4019 const bool timedOut =
174     (AnyPeriodicJobs()) ?
175     // do wait, but with a maximum timeout value
176     conditionJobsLeft.WaitIf(false, PERIODIC_JOBS_SEC, PERIODIC_JOBS_NSEC) :
177     // wait indefinitely
178     conditionJobsLeft.WaitIf(false);
179 schoenebeck 947 // reset flag
180 schoenebeck 4019 if (!timedOut) {
181     conditionJobsLeft.Set(false);
182     // unlock condition object so it can be turned again by other thread
183     conditionJobsLeft.Unlock();
184     }
185 schoenebeck 947 }
186 persson 1895 return 0;
187 schoenebeck 947 }
188 persson 1895
189 schoenebeck 4019 /**
190     * Schedule a job to be executed by instrument manager thread periodically.
191     *
192     * This method is intended to be used by external components to execute
193     * instrument files related tasks periodically. Currently this is hard
194     * coded to happen approximately every 200ms, however if the instrument
195     * manager thread is currently blocked by executing other tasks (e.g.
196     * loading some instrument) then this might also be significantly delayed
197     * accordingly.
198     *
199     * The scheduled task will continue to be executed indefinitely until
200     * @c RemovePeriodicJob() is called.
201     *
202     * Currently this method is only used by the SFZ engine for its auto
203     * reload feature (i.e. on modifications of .sfz files by external apps).
204     *
205     * @param name - arbitrary but unique ID for the task
206     * @param fn - actual callback for the task to be executed
207     */
208     void InstrumentManagerThread::AddPeriodicJob(String name, std::function<void()> fn) {
209     LockGuard lock(periodicJobsMutex);
210     RemovePeriodicJobWithoutLock(name);
211     periodicJobs.push_back({
212     .name = name,
213     .fn = fn
214     });
215     }
216    
217     /**
218     * Unschedule a periodic task previously scheduled by @c AddPeriodicJob().
219     *
220     * @param name - unique ID of the previously scheduled periodic task
221     */
222     void InstrumentManagerThread::RemovePeriodicJob(String name) {
223     LockGuard lock(periodicJobsMutex);
224     RemovePeriodicJobWithoutLock(name);
225     }
226    
227     void InstrumentManagerThread::RemovePeriodicJobWithoutLock(String name) {
228     for (size_t i = 0; i < periodicJobs.size(); ++i) {
229     if (periodicJobs[i].name == name) {
230     periodicJobs.erase( periodicJobs.begin() + i );
231     return;
232     }
233     }
234     }
235    
236     bool InstrumentManagerThread::AnyPeriodicJobs() {
237     // would probably be OK without a lock, but strictly it would be undefined behaviour
238     LockGuard lock(periodicJobsMutex);
239     return !periodicJobs.empty();
240     }
241    
242 iliev 1761 void InstrumentManagerThread::EventHandler::ChannelToBeRemoved(SamplerChannel* pChannel) {
243     /*
244     Removing from the queue an eventual scheduled loading of an instrument
245     to a sampler channel which is going to be removed.
246     */
247 persson 2427 LockGuard lock(pThread->mutex);
248 iliev 1761 std::list<command_t>::iterator it;
249     for (it = pThread->queue.begin(); it != pThread->queue.end();){
250     if ((*it).type != command_t::DIRECT_LOAD) { ++it; continue; }
251     if ((*it).pEngineChannel == pChannel->GetEngineChannel()) {
252     it = pThread->queue.erase(it);
253     // we don't break here because the same engine channel could
254     // occur more than once in the queue, so don't make optimizations
255     } else {
256     ++it;
257     }
258     }
259     }
260 persson 2185
261 persson 2351 #if defined(__APPLE__) && !defined(__x86_64__)
262 persson 2185 int InstrumentManagerThread::StopThread() {
263 persson 2351 // This is a fix for Mac OS X 32 bit, where SignalStopThread
264     // doesn't wake up a thread waiting for a condition variable.
265     SignalStopThread(); // send stop signal, but don't wait
266     conditionJobsLeft.Set(true); // wake thread
267 persson 2185 return Thread::StopThread(); // then wait for it to cancel
268     }
269 persson 2187 #endif
270 persson 2185
271 persson 2316 #ifdef WIN32
272     int InstrumentManagerThread::StopThread() {
273     int res = Thread::StopThread();
274     conditionJobsLeft.Reset();
275     return res;
276     }
277     #endif
278    
279 schoenebeck 947 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC