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