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 {
~BMidiRosterKillerBPrivate::BMidiRosterKiller49 ~BMidiRosterKiller()
50 {
51 delete sRoster;
52 }
53
CreateRosterBPrivate::BMidiRosterKiller54 static void CreateRoster() {
55 sRoster = new BMidiRoster();
56 }
57 }
58 midi_roster_killer;
59 }
60
61
62 BMidiEndpoint*
NextEndpoint(int32 * id)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*
NextProducer(int32 * id)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*
NextConsumer(int32 * id)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*
FindEndpoint(int32 id,bool localOnly)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*
FindProducer(int32 id,bool localOnly)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*
FindConsumer(int32 id,bool localOnly)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
StartWatching(const BMessenger * msngr)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
StopWatching()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
Register(BMidiEndpoint * endp)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
Unregister(BMidiEndpoint * endp)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*
MidiRoster()217 BMidiRoster::MidiRoster()
218 {
219 pthread_once(&sInitOnce, BPrivate::BMidiRosterKiller::CreateRoster);
220
221 return sRoster;
222 }
223
224
BMidiRoster()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
~BMidiRoster()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
_Reserved1()266 void BMidiRoster::_Reserved1() { }
_Reserved2()267 void BMidiRoster::_Reserved2() { }
_Reserved3()268 void BMidiRoster::_Reserved3() { }
_Reserved4()269 void BMidiRoster::_Reserved4() { }
_Reserved5()270 void BMidiRoster::_Reserved5() { }
_Reserved6()271 void BMidiRoster::_Reserved6() { }
_Reserved7()272 void BMidiRoster::_Reserved7() { }
_Reserved8()273 void BMidiRoster::_Reserved8() { }
274
275
276 void
CreateLocal(BMidiEndpoint * endp)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
DeleteLocal(BMidiEndpoint * endp)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
SendRequest(BMessage * msg,BMessage * reply)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