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