2 * Copyright 2006, Haiku.
4 * Copyright (c) 2002-2004 Matthijs Hollemans
5 * Distributed under the terms of the MIT License.
12 #include <MidiConsumer.h>
13 #include <MidiProducer.h>
14 #include <MidiRoster.h>
15 #include "MidiRosterLooper.h"
18 using namespace BPrivate
;
21 BMidiRosterLooper::BMidiRosterLooper()
22 : BLooper("MidiRosterLooper")
30 BMidiRosterLooper::~BMidiRosterLooper()
34 if (fInitLock
>= B_OK
) {
35 delete_sem(fInitLock
);
38 // At this point, our list may still contain endpoints with a
39 // zero reference count. These objects are proxies for remote
40 // endpoints, so we can safely delete them. If the list also
41 // contains endpoints with a non-zero refcount (which can be
42 // either remote or local), we will output a warning message.
43 // It would have been better to jump into the debugger, but I
44 // did not want to risk breaking any (misbehaving) old apps.
46 for (int32 t
= 0; t
< CountEndpoints(); ++t
) {
47 BMidiEndpoint
* endp
= EndpointAt(t
);
48 if (endp
->fRefCount
> 0) {
50 stderr
, "[midi] WARNING: Endpoint %" B_PRId32
" (%p) has "
51 "not been Release()d properly (refcount = %" B_PRId32
")\n",
52 endp
->ID(), endp
, endp
->fRefCount
);
61 BMidiRosterLooper::Init(BMidiRoster
* roster_
)
63 ASSERT(roster_
!= NULL
)
67 // We create a semaphore with a zero count. BMidiRoster's
68 // MidiRoster() method will try to acquire this semaphore,
69 // but blocks because the count is 0. When we receive the
70 // "app registered" message in our MessageReceived() hook,
71 // we release the semaphore and MidiRoster() will unblock.
73 fInitLock
= create_sem(0, "InitLock");
75 if (fInitLock
< B_OK
) {
76 WARN("Could not create semaphore")
80 thread_id threadId
= Run();
82 if (threadId
< B_OK
) {
83 WARN("Could not start looper thread")
92 BMidiRosterLooper::NextEndpoint(int32
* id
)
96 for (int32 t
= 0; t
< CountEndpoints(); ++t
) {
97 BMidiEndpoint
* endp
= EndpointAt(t
);
98 if (endp
->ID() > *id
) {
99 if (endp
->IsRemote() && endp
->IsRegistered()) {
111 BMidiRosterLooper::FindEndpoint(int32 id
)
113 for (int32 t
= 0; t
< CountEndpoints(); ++t
) {
114 BMidiEndpoint
* endp
= EndpointAt(t
);
115 if (endp
->ID() == id
) {
125 BMidiRosterLooper::AddEndpoint(BMidiEndpoint
* endp
)
128 ASSERT(!fEndpoints
.HasItem(endp
))
130 // We store the endpoints sorted by ID, because that
131 // simplifies the implementation of NextEndpoint().
132 // Although the midi_server assigns IDs in ascending
133 // order, we can't assume that the mNEW messages also
134 // are delivered in this order (mostly they will be).
137 for (t
= CountEndpoints(); t
> 0; --t
) {
138 BMidiEndpoint
* other
= EndpointAt(t
- 1);
139 if (endp
->ID() > other
->ID()) {
143 fEndpoints
.AddItem(endp
, t
);
152 BMidiRosterLooper::RemoveEndpoint(BMidiEndpoint
* endp
)
155 ASSERT(fEndpoints
.HasItem(endp
))
157 fEndpoints
.RemoveItem(endp
);
159 if (endp
->IsConsumer()) {
160 DisconnectDeadConsumer((BMidiConsumer
*) endp
);
162 DisconnectDeadProducer((BMidiProducer
*) endp
);
172 BMidiRosterLooper::StartWatching(const BMessenger
* watcher_
)
174 ASSERT(watcher_
!= NULL
)
177 fWatcher
= new BMessenger(*watcher_
);
185 BMidiRosterLooper::StopWatching()
193 BMidiRosterLooper::MessageReceived(BMessage
* msg
)
196 printf("IN "); msg
->PrintToStream();
200 case MSG_APP_REGISTERED
: OnAppRegistered(msg
); break;
201 case MSG_ENDPOINT_CREATED
: OnEndpointCreated(msg
); break;
202 case MSG_ENDPOINT_DELETED
: OnEndpointDeleted(msg
); break;
203 case MSG_ENDPOINT_CHANGED
: OnEndpointChanged(msg
); break;
204 case MSG_ENDPOINTS_CONNECTED
: OnConnectedDisconnected(msg
); break;
205 case MSG_ENDPOINTS_DISCONNECTED
: OnConnectedDisconnected(msg
); break;
207 default: super::MessageReceived(msg
); break;
213 BMidiRosterLooper::OnAppRegistered(BMessage
* msg
)
215 release_sem(fInitLock
);
220 BMidiRosterLooper::OnEndpointCreated(BMessage
* msg
)
228 if ((msg
->FindInt32("midi:id", &id
) == B_OK
)
229 && (msg
->FindBool("midi:registered", &isRegistered
) == B_OK
)
230 && (msg
->FindString("midi:name", &name
) == B_OK
)
231 && (msg
->FindMessage("midi:properties", &properties
) == B_OK
)
232 && (msg
->FindBool("midi:consumer", &isConsumer
) == B_OK
)) {
237 if ((msg
->FindInt32("midi:port", &port
) == B_OK
)
238 && (msg
->FindInt64("midi:latency", &latency
) == B_OK
)) {
239 BMidiConsumer
* cons
= new BMidiConsumer();
242 cons
->fIsRegistered
= isRegistered
;
244 cons
->fLatency
= latency
;
245 *(cons
->fProperties
) = properties
;
250 BMidiProducer
* prod
= new BMidiProducer();
253 prod
->fIsRegistered
= isRegistered
;
254 *(prod
->fProperties
) = properties
;
260 WARN("Could not create proxy for remote endpoint")
265 BMidiRosterLooper::OnEndpointDeleted(BMessage
* msg
)
268 if (msg
->FindInt32("midi:id", &id
) == B_OK
) {
269 BMidiEndpoint
* endp
= FindEndpoint(id
);
271 RemoveEndpoint(endp
);
273 // If the client is watching, and the endpoint is
274 // registered remote, we need to let it know that
275 // the endpoint is now unregistered.
277 if (endp
->IsRemote() && endp
->IsRegistered()) {
278 if (fWatcher
!= NULL
) {
280 notify
.AddInt32("be:op", B_MIDI_UNREGISTERED
);
281 ChangeEvent(¬ify
, endp
);
285 // If the proxy object for this endpoint is no
286 // longer being used, we can delete it. However,
287 // if the refcount is not zero, we must defer
288 // destruction until the client Release()'s the
289 // object. We clear the "isRegistered" flag to
290 // let the client know the object is now invalid.
292 if (endp
->fRefCount
== 0) {
294 } else { // still being used
295 endp
->fIsRegistered
= false;
296 endp
->fIsAlive
= false;
303 WARN("Could not delete proxy for remote endpoint")
308 BMidiRosterLooper::OnEndpointChanged(BMessage
* msg
)
311 if (msg
->FindInt32("midi:id", &id
) == B_OK
) {
312 BMidiEndpoint
* endp
= FindEndpoint(id
);
313 if ((endp
!= NULL
) && endp
->IsRemote()) {
314 ChangeRegistered(msg
, endp
);
315 ChangeName(msg
, endp
);
316 ChangeProperties(msg
, endp
);
317 ChangeLatency(msg
, endp
);
327 WARN("Could not change endpoint attributes")
332 BMidiRosterLooper::OnConnectedDisconnected(BMessage
* msg
)
334 int32 prodId
, consId
;
335 if ((msg
->FindInt32("midi:producer", &prodId
) == B_OK
)
336 && (msg
->FindInt32("midi:consumer", &consId
) == B_OK
)) {
337 BMidiEndpoint
* endp1
= FindEndpoint(prodId
);
338 BMidiEndpoint
* endp2
= FindEndpoint(consId
);
340 if ((endp1
!= NULL
) && endp1
->IsProducer()) {
341 if ((endp2
!= NULL
) && endp2
->IsConsumer()) {
342 BMidiProducer
* prod
= (BMidiProducer
*) endp1
;
343 BMidiConsumer
* cons
= (BMidiConsumer
*) endp2
;
345 bool mustConnect
= (msg
->what
== MSG_ENDPOINTS_CONNECTED
);
348 prod
->ConnectionMade(cons
);
350 prod
->ConnectionBroken(cons
);
353 if (fWatcher
!= NULL
) {
354 ConnectionEvent(prod
, cons
, mustConnect
);
366 WARN("Could not connect/disconnect endpoints")
371 BMidiRosterLooper::ChangeRegistered(BMessage
* msg
, BMidiEndpoint
* endp
)
377 if (msg
->FindBool("midi:registered", &isRegistered
) == B_OK
) {
378 if (endp
->fIsRegistered
!= isRegistered
) {
379 endp
->fIsRegistered
= isRegistered
;
381 if (fWatcher
!= NULL
) {
384 notify
.AddInt32("be:op", B_MIDI_REGISTERED
);
386 notify
.AddInt32("be:op", B_MIDI_UNREGISTERED
);
388 ChangeEvent(¬ify
, endp
);
396 BMidiRosterLooper::ChangeName(BMessage
* msg
, BMidiEndpoint
* endp
)
402 if (msg
->FindString("midi:name", &name
) == B_OK
) {
403 if (endp
->fName
!= name
) {
406 if ((fWatcher
!= NULL
) && endp
->IsRegistered()) {
408 notify
.AddInt32("be:op", B_MIDI_CHANGED_NAME
);
409 notify
.AddString("be:name", name
);
410 ChangeEvent(¬ify
, endp
);
418 BMidiRosterLooper::ChangeProperties(BMessage
* msg
, BMidiEndpoint
* endp
)
424 if (msg
->FindMessage("midi:properties", &properties
) == B_OK
) {
425 *(endp
->fProperties
) = properties
;
427 if ((fWatcher
!= NULL
) && endp
->IsRegistered()) {
429 notify
.AddInt32("be:op", B_MIDI_CHANGED_PROPERTIES
);
430 notify
.AddMessage("be:properties", &properties
);
431 ChangeEvent(¬ify
, endp
);
438 BMidiRosterLooper::ChangeLatency(BMessage
* msg
, BMidiEndpoint
* endp
)
444 if (msg
->FindInt64("midi:latency", &latency
) == B_OK
) {
445 if (endp
->IsConsumer()) {
446 BMidiConsumer
* cons
= (BMidiConsumer
*) endp
;
447 if (cons
->fLatency
!= latency
) {
448 cons
->fLatency
= latency
;
450 if ((fWatcher
!= NULL
) && cons
->IsRegistered()) {
452 notify
.AddInt32("be:op", B_MIDI_CHANGED_LATENCY
);
453 notify
.AddInt64("be:latency", latency
);
454 ChangeEvent(¬ify
, endp
);
463 BMidiRosterLooper::AllEndpoints()
466 for (int32 t
= 0; t
< CountEndpoints(); ++t
) {
467 BMidiEndpoint
* endp
= EndpointAt(t
);
468 if (endp
->IsRemote() && endp
->IsRegistered()) {
470 notify
.AddInt32("be:op", B_MIDI_REGISTERED
);
471 ChangeEvent(¬ify
, endp
);
478 BMidiRosterLooper::AllConnections()
480 for (int32 t
= 0; t
< CountEndpoints(); ++t
) {
481 BMidiEndpoint
* endp
= EndpointAt(t
);
482 if (endp
->IsRemote() && endp
->IsRegistered()) {
483 if (endp
->IsProducer()) {
484 BMidiProducer
* prod
= (BMidiProducer
*) endp
;
485 if (prod
->LockProducer()) {
486 for (int32 k
= 0; k
< prod
->CountConsumers(); ++k
) {
487 ConnectionEvent(prod
, prod
->ConsumerAt(k
), true);
489 prod
->UnlockProducer();
498 BMidiRosterLooper::ChangeEvent(BMessage
* msg
, BMidiEndpoint
* endp
)
500 ASSERT(fWatcher
!= NULL
)
504 msg
->what
= B_MIDI_EVENT
;
505 msg
->AddInt32("be:id", endp
->ID());
507 if (endp
->IsConsumer()) {
508 msg
->AddString("be:type", "consumer");
510 msg
->AddString("be:type", "producer");
513 fWatcher
->SendMessage(msg
);
518 BMidiRosterLooper::ConnectionEvent(
519 BMidiProducer
* prod
, BMidiConsumer
* cons
, bool mustConnect
)
521 ASSERT(fWatcher
!= NULL
)
526 notify
.what
= B_MIDI_EVENT
;
527 notify
.AddInt32("be:producer", prod
->ID());
528 notify
.AddInt32("be:consumer", cons
->ID());
531 notify
.AddInt32("be:op", B_MIDI_CONNECTED
);
533 notify
.AddInt32("be:op", B_MIDI_DISCONNECTED
);
536 fWatcher
->SendMessage(¬ify
);
541 BMidiRosterLooper::DisconnectDeadConsumer(BMidiConsumer
* cons
)
545 // Note: Rather than looping through each producer's list
546 // of connected consumers, we let ConnectionBroken() tell
547 // us whether the consumer really was connected.
549 for (int32 t
= 0; t
< CountEndpoints(); ++t
) {
550 BMidiEndpoint
* endp
= EndpointAt(t
);
551 if (endp
->IsProducer()) {
552 BMidiProducer
* prod
= (BMidiProducer
*) endp
;
553 if (prod
->ConnectionBroken(cons
)) {
554 if (cons
->IsRemote() && (fWatcher
!= NULL
)) {
555 ConnectionEvent(prod
, cons
, false);
564 BMidiRosterLooper::DisconnectDeadProducer(BMidiProducer
* prod
)
568 // We don't need to lock or remove the consumers from
569 // the producer's list of connections, because when this
570 // function is called, we're destroying the object.
572 if (prod
->IsRemote() && (fWatcher
!= NULL
)) {
573 for (int32 t
= 0; t
< prod
->CountConsumers(); ++t
) {
574 ConnectionEvent(prod
, prod
->ConsumerAt(t
), false);
581 BMidiRosterLooper::CountEndpoints()
583 return fEndpoints
.CountItems();
588 BMidiRosterLooper::EndpointAt(int32 index
)
590 ASSERT(index
>= 0 && index
< CountEndpoints())
592 return (BMidiEndpoint
*) fEndpoints
.ItemAt(index
);
598 BMidiRosterLooper::DumpEndpoints()
601 printf("*** START DumpEndpoints\n");
603 for (int32 t
= 0; t
< CountEndpoints(); ++t
) {
604 BMidiEndpoint
* endp
= EndpointAt(t
);
606 printf("\tendpoint %" B_PRId32
" (%p):\n", t
, endp
);
609 "\t\tid %" B_PRId32
", name '%s', %s, %s, %s, %s, refcount %"
610 B_PRId32
"\n", endp
->ID(), endp
->Name(),
611 endp
->IsConsumer() ? "consumer" : "producer",
612 endp
->IsRegistered() ? "registered" : "unregistered",
613 endp
->IsLocal() ? "local" : "remote",
614 endp
->IsValid() ? "valid" : "invalid", endp
->fRefCount
);
616 printf("\t\tproperties: ");
617 endp
->fProperties
->PrintToStream();
619 if (endp
->IsConsumer()) {
620 BMidiConsumer
* cons
= (BMidiConsumer
*) endp
;
621 printf("\t\tport %" B_PRId32
", latency %" B_PRIdBIGTIME
"\n",
622 cons
->fPort
, cons
->fLatency
);
624 BMidiProducer
* prod
= (BMidiProducer
*) endp
;
625 if (prod
->LockProducer()) {
626 printf("\t\tconnections:\n");
627 for (int32 k
= 0; k
< prod
->CountConsumers(); ++k
) {
628 BMidiConsumer
* cons
= prod
->ConsumerAt(k
);
629 printf("\t\t\tid %" B_PRId32
" (%p)\n", cons
->ID(),
632 prod
->UnlockProducer();
637 printf("*** END DumpEndpoints\n");