* Copyright 2006, Haiku.
*
* Copyright (c) 2002-2004 Matthijs Hollemans
* Distributed under the terms of the MIT License.
*
* Authors:
* Matthijs Hollemans
*/
#include "debug.h"
#include <MidiConsumer.h>
#include <MidiProducer.h>
#include <MidiRoster.h>
#include "MidiRosterLooper.h"
#include "protocol.h"
using namespace BPrivate;
BMidiRosterLooper::BMidiRosterLooper()
: BLooper("MidiRosterLooper")
{
fInitLock = -1;
fRoster = NULL;
fWatcher = NULL;
}
BMidiRosterLooper::~BMidiRosterLooper()
{
StopWatching();
if (fInitLock >= B_OK) {
delete_sem(fInitLock);
}
for (int32 t = 0; t < CountEndpoints(); ++t) {
BMidiEndpoint* endp = EndpointAt(t);
if (endp->fRefCount > 0) {
fprintf(
stderr, "[midi] WARNING: Endpoint %" B_PRId32 " (%p) has "
"not been Release()d properly (refcount = %" B_PRId32 ")\n",
endp->ID(), endp, endp->fRefCount);
} else {
delete endp;
}
}
}
bool
BMidiRosterLooper::Init(BMidiRoster* roster_)
{
ASSERT(roster_ != NULL)
fRoster = roster_;
fInitLock = create_sem(0, "InitLock");
if (fInitLock < B_OK) {
WARN("Could not create semaphore")
return false;
}
thread_id threadId = Run();
if (threadId < B_OK) {
WARN("Could not start looper thread")
return false;
}
return true;
}
BMidiEndpoint*
BMidiRosterLooper::NextEndpoint(int32* id)
{
ASSERT(id != NULL)
for (int32 t = 0; t < CountEndpoints(); ++t) {
BMidiEndpoint* endp = EndpointAt(t);
if (endp->ID() > *id) {
if (endp->IsRemote() && endp->IsRegistered()) {
*id = endp->ID();
return endp;
}
}
}
return NULL;
}
BMidiEndpoint*
BMidiRosterLooper::FindEndpoint(int32 id)
{
for (int32 t = 0; t < CountEndpoints(); ++t) {
BMidiEndpoint* endp = EndpointAt(t);
if (endp->ID() == id) {
return endp;
}
}
return NULL;
}
void
BMidiRosterLooper::AddEndpoint(BMidiEndpoint* endp)
{
ASSERT(endp != NULL)
ASSERT(!fEndpoints.HasItem(endp))
int32 t;
for (t = CountEndpoints(); t > 0; --t) {
BMidiEndpoint* other = EndpointAt(t - 1);
if (endp->ID() > other->ID()) {
break;
}
}
fEndpoints.AddItem(endp, t);
#ifdef DEBUG
DumpEndpoints();
#endif
}
void
BMidiRosterLooper::RemoveEndpoint(BMidiEndpoint* endp)
{
ASSERT(endp != NULL)
ASSERT(fEndpoints.HasItem(endp))
fEndpoints.RemoveItem(endp);
if (endp->IsConsumer()) {
DisconnectDeadConsumer((BMidiConsumer*) endp);
} else {
DisconnectDeadProducer((BMidiProducer*) endp);
}
#ifdef DEBUG
DumpEndpoints();
#endif
}
void
BMidiRosterLooper::StartWatching(const BMessenger* watcher_)
{
ASSERT(watcher_ != NULL)
StopWatching();
fWatcher = new BMessenger(*watcher_);
AllEndpoints();
AllConnections();
}
void
BMidiRosterLooper::StopWatching()
{
delete fWatcher;
fWatcher = NULL;
}
void
BMidiRosterLooper::MessageReceived(BMessage* msg)
{
#ifdef DEBUG
printf("IN "); msg->PrintToStream();
#endif
switch (msg->what) {
case MSG_APP_REGISTERED: OnAppRegistered(msg); break;
case MSG_ENDPOINT_CREATED: OnEndpointCreated(msg); break;
case MSG_ENDPOINT_DELETED: OnEndpointDeleted(msg); break;
case MSG_ENDPOINT_CHANGED: OnEndpointChanged(msg); break;
case MSG_ENDPOINTS_CONNECTED: OnConnectedDisconnected(msg); break;
case MSG_ENDPOINTS_DISCONNECTED: OnConnectedDisconnected(msg); break;
default: super::MessageReceived(msg); break;
}
}
void
BMidiRosterLooper::OnAppRegistered(BMessage* msg)
{
release_sem(fInitLock);
}
void
BMidiRosterLooper::OnEndpointCreated(BMessage* msg)
{
int32 id;
bool isRegistered;
BString name;
BMessage properties;
bool isConsumer;
if ((msg->FindInt32("midi:id", &id) == B_OK)
&& (msg->FindBool("midi:registered", &isRegistered) == B_OK)
&& (msg->FindString("midi:name", &name) == B_OK)
&& (msg->FindMessage("midi:properties", &properties) == B_OK)
&& (msg->FindBool("midi:consumer", &isConsumer) == B_OK)) {
if (isConsumer) {
int32 port;
bigtime_t latency;
if ((msg->FindInt32("midi:port", &port) == B_OK)
&& (msg->FindInt64("midi:latency", &latency) == B_OK)) {
BMidiConsumer* cons = new BMidiConsumer();
cons->fName = name;
cons->fId = id;
cons->fIsRegistered = isRegistered;
cons->fPort = port;
cons->fLatency = latency;
*(cons->fProperties) = properties;
AddEndpoint(cons);
return;
}
} else {
BMidiProducer* prod = new BMidiProducer();
prod->fName = name;
prod->fId = id;
prod->fIsRegistered = isRegistered;
*(prod->fProperties) = properties;
AddEndpoint(prod);
return;
}
}
WARN("Could not create proxy for remote endpoint")
}
void
BMidiRosterLooper::OnEndpointDeleted(BMessage* msg)
{
int32 id;
if (msg->FindInt32("midi:id", &id) == B_OK) {
BMidiEndpoint* endp = FindEndpoint(id);
if (endp != NULL) {
RemoveEndpoint(endp);
if (endp->IsRemote() && endp->IsRegistered()) {
if (fWatcher != NULL) {
BMessage notify;
notify.AddInt32("be:op", B_MIDI_UNREGISTERED);
ChangeEvent(¬ify, endp);
}
}
if (endp->fRefCount == 0) {
delete endp;
} else {
endp->fIsRegistered = false;
endp->fIsAlive = false;
}
return;
}
}
WARN("Could not delete proxy for remote endpoint")
}
void
BMidiRosterLooper::OnEndpointChanged(BMessage* msg)
{
int32 id;
if (msg->FindInt32("midi:id", &id) == B_OK) {
BMidiEndpoint* endp = FindEndpoint(id);
if ((endp != NULL) && endp->IsRemote()) {
ChangeRegistered(msg, endp);
ChangeName(msg, endp);
ChangeProperties(msg, endp);
ChangeLatency(msg, endp);
#ifdef DEBUG
DumpEndpoints();
#endif
return;
}
}
WARN("Could not change endpoint attributes")
}
void
BMidiRosterLooper::OnConnectedDisconnected(BMessage* msg)
{
int32 prodId, consId;
if ((msg->FindInt32("midi:producer", &prodId) == B_OK)
&& (msg->FindInt32("midi:consumer", &consId) == B_OK)) {
BMidiEndpoint* endp1 = FindEndpoint(prodId);
BMidiEndpoint* endp2 = FindEndpoint(consId);
if ((endp1 != NULL) && endp1->IsProducer()) {
if ((endp2 != NULL) && endp2->IsConsumer()) {
BMidiProducer* prod = (BMidiProducer*) endp1;
BMidiConsumer* cons = (BMidiConsumer*) endp2;
bool mustConnect = (msg->what == MSG_ENDPOINTS_CONNECTED);
if (mustConnect) {
prod->ConnectionMade(cons);
} else {
prod->ConnectionBroken(cons);
}
if (fWatcher != NULL) {
ConnectionEvent(prod, cons, mustConnect);
}
#ifdef DEBUG
DumpEndpoints();
#endif
return;
}
}
}
WARN("Could not connect/disconnect endpoints")
}
void
BMidiRosterLooper::ChangeRegistered(BMessage* msg, BMidiEndpoint* endp)
{
ASSERT(msg != NULL)
ASSERT(endp != NULL)
bool isRegistered;
if (msg->FindBool("midi:registered", &isRegistered) == B_OK) {
if (endp->fIsRegistered != isRegistered) {
endp->fIsRegistered = isRegistered;
if (fWatcher != NULL) {
BMessage notify;
if (isRegistered) {
notify.AddInt32("be:op", B_MIDI_REGISTERED);
} else {
notify.AddInt32("be:op", B_MIDI_UNREGISTERED);
}
ChangeEvent(¬ify, endp);
}
}
}
}
void
BMidiRosterLooper::ChangeName(BMessage* msg, BMidiEndpoint* endp)
{
ASSERT(msg != NULL)
ASSERT(endp != NULL)
BString name;
if (msg->FindString("midi:name", &name) == B_OK) {
if (endp->fName != name) {
endp->fName = name;
if ((fWatcher != NULL) && endp->IsRegistered()) {
BMessage notify;
notify.AddInt32("be:op", B_MIDI_CHANGED_NAME);
notify.AddString("be:name", name);
ChangeEvent(¬ify, endp);
}
}
}
}
void
BMidiRosterLooper::ChangeProperties(BMessage* msg, BMidiEndpoint* endp)
{
ASSERT(msg != NULL)
ASSERT(endp != NULL)
BMessage properties;
if (msg->FindMessage("midi:properties", &properties) == B_OK) {
*(endp->fProperties) = properties;
if ((fWatcher != NULL) && endp->IsRegistered()) {
BMessage notify;
notify.AddInt32("be:op", B_MIDI_CHANGED_PROPERTIES);
notify.AddMessage("be:properties", &properties);
ChangeEvent(¬ify, endp);
}
}
}
void
BMidiRosterLooper::ChangeLatency(BMessage* msg, BMidiEndpoint* endp)
{
ASSERT(msg != NULL)
ASSERT(endp != NULL)
bigtime_t latency;
if (msg->FindInt64("midi:latency", &latency) == B_OK) {
if (endp->IsConsumer()) {
BMidiConsumer* cons = (BMidiConsumer*) endp;
if (cons->fLatency != latency) {
cons->fLatency = latency;
if ((fWatcher != NULL) && cons->IsRegistered()) {
BMessage notify;
notify.AddInt32("be:op", B_MIDI_CHANGED_LATENCY);
notify.AddInt64("be:latency", latency);
ChangeEvent(¬ify, endp);
}
}
}
}
}
void
BMidiRosterLooper::AllEndpoints()
{
BMessage notify;
for (int32 t = 0; t < CountEndpoints(); ++t) {
BMidiEndpoint* endp = EndpointAt(t);
if (endp->IsRemote() && endp->IsRegistered()) {
notify.MakeEmpty();
notify.AddInt32("be:op", B_MIDI_REGISTERED);
ChangeEvent(¬ify, endp);
}
}
}
void
BMidiRosterLooper::AllConnections()
{
for (int32 t = 0; t < CountEndpoints(); ++t) {
BMidiEndpoint* endp = EndpointAt(t);
if (endp->IsRemote() && endp->IsRegistered()) {
if (endp->IsProducer()) {
BMidiProducer* prod = (BMidiProducer*) endp;
if (prod->LockProducer()) {
for (int32 k = 0; k < prod->CountConsumers(); ++k) {
ConnectionEvent(prod, prod->ConsumerAt(k), true);
}
prod->UnlockProducer();
}
}
}
}
}
void
BMidiRosterLooper::ChangeEvent(BMessage* msg, BMidiEndpoint* endp)
{
ASSERT(fWatcher != NULL)
ASSERT(msg != NULL)
ASSERT(endp != NULL)
msg->what = B_MIDI_EVENT;
msg->AddInt32("be:id", endp->ID());
if (endp->IsConsumer()) {
msg->AddString("be:type", "consumer");
} else {
msg->AddString("be:type", "producer");
}
fWatcher->SendMessage(msg);
}
void
BMidiRosterLooper::ConnectionEvent(
BMidiProducer* prod, BMidiConsumer* cons, bool mustConnect)
{
ASSERT(fWatcher != NULL)
ASSERT(prod != NULL)
ASSERT(cons != NULL)
BMessage notify;
notify.what = B_MIDI_EVENT;
notify.AddInt32("be:producer", prod->ID());
notify.AddInt32("be:consumer", cons->ID());
if (mustConnect) {
notify.AddInt32("be:op", B_MIDI_CONNECTED);
} else {
notify.AddInt32("be:op", B_MIDI_DISCONNECTED);
}
fWatcher->SendMessage(¬ify);
}
void
BMidiRosterLooper::DisconnectDeadConsumer(BMidiConsumer* cons)
{
ASSERT(cons != NULL)
for (int32 t = 0; t < CountEndpoints(); ++t) {
BMidiEndpoint* endp = EndpointAt(t);
if (endp->IsProducer()) {
BMidiProducer* prod = (BMidiProducer*) endp;
if (prod->ConnectionBroken(cons)) {
if (cons->IsRemote() && (fWatcher != NULL)) {
ConnectionEvent(prod, cons, false);
}
}
}
}
}
void
BMidiRosterLooper::DisconnectDeadProducer(BMidiProducer* prod)
{
ASSERT(prod != NULL)
if (prod->IsRemote() && (fWatcher != NULL)) {
for (int32 t = 0; t < prod->CountConsumers(); ++t) {
ConnectionEvent(prod, prod->ConsumerAt(t), false);
}
}
}
int32
BMidiRosterLooper::CountEndpoints()
{
return fEndpoints.CountItems();
}
BMidiEndpoint*
BMidiRosterLooper::EndpointAt(int32 index)
{
ASSERT(index >= 0 && index < CountEndpoints())
return (BMidiEndpoint*) fEndpoints.ItemAt(index);
}
#ifdef DEBUG
void
BMidiRosterLooper::DumpEndpoints()
{
if (Lock()) {
printf("*** START DumpEndpoints\n");
for (int32 t = 0; t < CountEndpoints(); ++t) {
BMidiEndpoint* endp = EndpointAt(t);
printf("\tendpoint %" B_PRId32 " (%p):\n", t, endp);
printf(
"\t\tid %" B_PRId32 ", name '%s', %s, %s, %s, %s, refcount %"
B_PRId32 "\n", endp->ID(), endp->Name(),
endp->IsConsumer() ? "consumer" : "producer",
endp->IsRegistered() ? "registered" : "unregistered",
endp->IsLocal() ? "local" : "remote",
endp->IsValid() ? "valid" : "invalid", endp->fRefCount);
printf("\t\tproperties: ");
endp->fProperties->PrintToStream();
if (endp->IsConsumer()) {
BMidiConsumer* cons = (BMidiConsumer*) endp;
printf("\t\tport %" B_PRId32 ", latency %" B_PRIdBIGTIME "\n",
cons->fPort, cons->fLatency);
} else {
BMidiProducer* prod = (BMidiProducer*) endp;
if (prod->LockProducer()) {
printf("\t\tconnections:\n");
for (int32 k = 0; k < prod->CountConsumers(); ++k) {
BMidiConsumer* cons = prod->ConsumerAt(k);
printf("\t\t\tid %" B_PRId32 " (%p)\n", cons->ID(),
cons);
}
prod->UnlockProducer();
}
}
}
printf("*** END DumpEndpoints\n");
Unlock();
}
}
#endif