xref: /haiku/src/kits/midi2/MidiRoster.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 /*
2  * Copyright (c) 2002-2003 Matthijs Hollemans
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "debug.h"
24 #include "MidiConsumer.h"
25 #include "MidiRoster.h"
26 #include "MidiRosterLooper.h"
27 #include "protocol.h"
28 
29 // The midi_debug_level and midi_dispatcher_priority symbols
30 // were exported by Be's libmidi2, and even though they do not
31 // appear in the headers, some apps may still be using them.
32 // For backwards compatibility's sake, we export those symbols
33 // as well, even though we do not use them for anything.
34 
35 // Not used. For backwards compatibility only.
36 int32 midi_debug_level = 0;
37 
38 // Not used. For backwards compatibility only.
39 int32 midi_dispatcher_priority = B_REAL_TIME_PRIORITY;
40 
41 //------------------------------------------------------------------------------
42 
43 // The one and only BMidiRoster instance, which is created
44 // the first time the client app calls MidiRoster(). It is
45 // destroyed by the BMidiRosterKiller when the app quits.
46 static BMidiRoster* roster = NULL;
47 
48 // Destroys the BMidiRoster instance when the app quits.
49 static class BMidiRosterKiller
50 {
51 public:
52 	~BMidiRosterKiller()
53 	{
54 		delete roster;
55 	}
56 }
57 killer;
58 
59 //------------------------------------------------------------------------------
60 
61 BMidiEndpoint* BMidiRoster::NextEndpoint(int32* id)
62 {
63 	BMidiEndpoint* endp = NULL;
64 
65 	if (id != NULL)
66 	{
67 		BMidiRosterLooper* looper = MidiRoster()->looper;
68 		if (looper->Lock())
69 		{
70 			endp = looper->NextEndpoint(id);
71 			if (endp != NULL)
72 			{
73 				endp->Acquire();
74 			}
75 			looper->Unlock();
76 		}
77 	}
78 
79 	return endp;
80 }
81 
82 //------------------------------------------------------------------------------
83 
84 BMidiProducer* BMidiRoster::NextProducer(int32* id)
85 {
86 	BMidiEndpoint* endp;
87 
88 	while ((endp = NextEndpoint(id)) != NULL)
89 	{
90 		if (endp->IsProducer())
91 		{
92 			return (BMidiProducer*) endp;
93 		}
94 		else
95 		{
96 			endp->Release();
97 		}
98 	}
99 
100 	return NULL;
101 }
102 
103 //------------------------------------------------------------------------------
104 
105 BMidiConsumer* BMidiRoster::NextConsumer(int32* id)
106 {
107 	BMidiEndpoint* endp;
108 
109 	while ((endp = NextEndpoint(id)) != NULL)
110 	{
111 		if (endp->IsConsumer())
112 		{
113 			return (BMidiConsumer*) endp;
114 		}
115 		else
116 		{
117 			endp->Release();
118 		}
119 	}
120 
121 	return NULL;
122 }
123 
124 //------------------------------------------------------------------------------
125 
126 BMidiEndpoint* BMidiRoster::FindEndpoint(int32 id, bool localOnly)
127 {
128 	BMidiEndpoint* endp = NULL;
129 
130 	BMidiRosterLooper* looper = MidiRoster()->looper;
131 	if (looper->Lock())
132 	{
133 		endp = looper->FindEndpoint(id);
134 
135 		if ((endp != NULL) && endp->IsRemote())
136 		{
137 			if (localOnly || !endp->IsRegistered())
138 			{
139 				endp = NULL;
140 			}
141 		}
142 
143 		if (endp != NULL)
144 		{
145 			endp->Acquire();
146 		}
147 
148 		looper->Unlock();
149 	}
150 
151 	return endp;
152 }
153 
154 //------------------------------------------------------------------------------
155 
156 BMidiProducer* BMidiRoster::FindProducer(int32 id, bool localOnly)
157 {
158 	BMidiEndpoint* endp = FindEndpoint(id, localOnly);
159 
160 	if ((endp != NULL) && !endp->IsProducer())
161 	{
162 		endp->Release();
163 		endp = NULL;
164 	}
165 
166 	return (BMidiProducer*) endp;
167 }
168 
169 //------------------------------------------------------------------------------
170 
171 BMidiConsumer* BMidiRoster::FindConsumer(int32 id, bool localOnly)
172 {
173 	BMidiEndpoint* endp = FindEndpoint(id, localOnly);
174 
175 	if ((endp != NULL) && !endp->IsConsumer())
176 	{
177 		endp->Release();
178 		endp = NULL;
179 	}
180 
181 	return (BMidiConsumer*) endp;
182 }
183 
184 //------------------------------------------------------------------------------
185 
186 void BMidiRoster::StartWatching(const BMessenger* msngr)
187 {
188 	if (msngr == NULL)
189 	{
190 		WARN("StartWatching does not accept a NULL messenger")
191 	}
192 	else
193 	{
194 		BMidiRosterLooper* looper = MidiRoster()->looper;
195 		if (looper->Lock())
196 		{
197 			looper->StartWatching(msngr);
198 			looper->Unlock();
199 		}
200 	}
201 }
202 
203 //------------------------------------------------------------------------------
204 
205 void BMidiRoster::StopWatching()
206 {
207 	BMidiRosterLooper* looper = MidiRoster()->looper;
208 	if (looper->Lock())
209 	{
210 		looper->StopWatching();
211 		looper->Unlock();
212 	}
213 }
214 
215 //------------------------------------------------------------------------------
216 
217 status_t BMidiRoster::Register(BMidiEndpoint* endp)
218 {
219 	if (endp != NULL)
220 	{
221 		return endp->Register();
222 	}
223 
224 	return B_BAD_VALUE;
225 }
226 
227 //------------------------------------------------------------------------------
228 
229 status_t BMidiRoster::Unregister(BMidiEndpoint* endp)
230 {
231 	if (endp != NULL)
232 	{
233 		return endp->Unregister();
234 	}
235 
236 	return B_BAD_VALUE;
237 }
238 
239 //------------------------------------------------------------------------------
240 
241 BMidiRoster* BMidiRoster::MidiRoster()
242 {
243 	if (roster == NULL)
244 	{
245 		new BMidiRoster();
246 	}
247 
248 	return roster;
249 }
250 
251 //------------------------------------------------------------------------------
252 
253 BMidiRoster::BMidiRoster()
254 {
255 	TRACE(("BMidiRoster::BMidiRoster"))
256 
257 	// While our constructor is executing, some function may
258 	// call MidiRoster() again, which causes an endless loop.
259 	// To prevent this, we immediately fill in "roster"; now
260 	// subsequent calls to MidiRoster() won't mess up things.
261 
262 	roster = this;
263 
264 	server = new BMessenger(MIDI_SERVER_SIGNATURE);
265 	looper = new BMidiRosterLooper();
266 
267 	if (!looper->Init(this)) { return; }
268 
269 	BMessage msg;
270 	msg.what = MSG_REGISTER_APP;
271 	msg.AddMessenger("midi:messenger", BMessenger(looper));
272 
273 	if (server->SendMessage(&msg, looper, TIMEOUT) != B_OK)
274 	{
275 		WARN("Cannot send request to midi_server");
276 		return;
277 	}
278 
279 	// Although unlikely, we may receive the midi_server's
280 	// "app registered" reply before we lock the semaphore.
281 	// In that case, BMidiRosterLooper's MessageReceived()
282 	// will bump the semaphore count, and our acquire_sem()
283 	// can grab the semaphore safely (without blocking).
284 
285 	acquire_sem(looper->initLock);
286 }
287 
288 //------------------------------------------------------------------------------
289 
290 BMidiRoster::~BMidiRoster()
291 {
292 	TRACE(("BMidiRoster::~BMidiRoster"))
293 
294 	if (looper != NULL)
295 	{
296 		looper->Lock();
297 		looper->Quit();
298 	}
299 
300 	delete server;
301 }
302 
303 //------------------------------------------------------------------------------
304 
305 void BMidiRoster::_Reserved1() { }
306 void BMidiRoster::_Reserved2() { }
307 void BMidiRoster::_Reserved3() { }
308 void BMidiRoster::_Reserved4() { }
309 void BMidiRoster::_Reserved5() { }
310 void BMidiRoster::_Reserved6() { }
311 void BMidiRoster::_Reserved7() { }
312 void BMidiRoster::_Reserved8() { }
313 
314 //------------------------------------------------------------------------------
315 
316 void BMidiRoster::CreateLocal(BMidiEndpoint* endp)
317 {
318 	ASSERT(endp != NULL)
319 
320 	// We are being called from the BMidiLocalConsumer or
321 	// BMidiLocalProducer constructor, so there is no need
322 	// to lock anything, because at this point there cannot
323 	// be multiple threads accessing the endpoint's data.
324 
325 	BMessage msg;
326 	msg.what = MSG_CREATE_ENDPOINT;
327 	msg.AddBool("midi:consumer", endp->isConsumer);
328 	msg.AddBool("midi:registered", endp->isRegistered);
329 	msg.AddString("midi:name", endp->Name());
330 	msg.AddMessage("midi:properties", endp->properties);
331 
332 	if (endp->IsConsumer())
333 	{
334 		BMidiConsumer* consumer = (BMidiConsumer*) endp;
335 		msg.AddInt32("midi:port", consumer->port);
336 		msg.AddInt64("midi:latency", consumer->latency);
337 	}
338 
339 	BMessage reply;
340 	if (SendRequest(&msg, &reply) == B_OK)
341 	{
342 		status_t res;
343 		if (reply.FindInt32("midi:result", &res) == B_OK)
344 		{
345 			if (res == B_OK)
346 			{
347 				int32 id;
348 				if (reply.FindInt32("midi:id", &id) == B_OK)
349 				{
350 					endp->id = id;
351 
352 					if (looper->Lock())
353 					{
354 						looper->AddEndpoint(endp);
355 						looper->Unlock();
356 					}
357 				}
358 			}
359 		}
360 	}
361 
362 	// There are many things that can go wrong when creating
363 	// a new endpoint, but BMidiEndpoint has no InitCheck()
364 	// method to check for this. (You can, however, see if the
365 	// endpoint's ID is 0 after the constructor returns, or
366 	// call IsValid().) In any case, you should still Release()
367 	// the endpoint to delete the object. (This is different
368 	// from Be's implementation, which bumps the refcount only
369 	// when creation succeeded. If you call Release(), you'll
370 	// trip an assertion, so you can't delete these endpoints.)
371 }
372 
373 //------------------------------------------------------------------------------
374 
375 void BMidiRoster::DeleteLocal(BMidiEndpoint* endp)
376 {
377 	ASSERT(endp != NULL)
378 
379 	BMessage msg;
380 	msg.what = MSG_DELETE_ENDPOINT;
381 	msg.AddInt32("midi:id", endp->ID());
382 
383 	// Note: this is always called from BMidiLocalConsumer's
384 	// or BMidiLocalProducer's destructor, so we don't expect
385 	// a reply from the server. If something went wrong, the
386 	// object will be destroyed regardless.
387 
388 	server->SendMessage(&msg, (BHandler*) NULL, TIMEOUT);
389 
390 	// If the endpoint was successfully created, we must remove
391 	// it from the list of endpoints. If creation failed, then
392 	// we didn't put the endpoint on that list. If the endpoint
393 	// was connected to anything, we must also disconnect it.
394 
395 	if (endp->ID() > 0)
396 	{
397 		if (looper->Lock())
398 		{
399 			looper->RemoveEndpoint(endp);
400 			looper->Unlock();
401 		}
402 	}
403 }
404 
405 //------------------------------------------------------------------------------
406 
407 status_t BMidiRoster::SendRequest(BMessage* msg, BMessage* reply)
408 {
409 	ASSERT(msg != NULL)
410 	ASSERT(reply != NULL)
411 
412 	status_t err = server->SendMessage(msg, reply, TIMEOUT, TIMEOUT);
413 
414 	if (err != B_OK)
415 	{
416 		WARN("Cannot send request to midi_server");
417 	}
418 
419 	#ifdef DEBUG
420 	if (err == B_OK)
421 	{
422 		printf("REPLY "); reply->PrintToStream();
423 	}
424 	#endif
425 
426 	return err;
427 }
428 
429 //------------------------------------------------------------------------------
430