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