2003-03-02 11:48:26 +00:00
|
|
|
/*
|
2004-05-13 10:55:51 +00:00
|
|
|
* Copyright (c) 2002-2004 Matthijs Hollemans
|
2003-03-11 20:38:00 +00:00
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
* DEALINGS IN THE SOFTWARE.
|
2002-12-31 14:34:17 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "debug.h"
|
|
|
|
|
#include "MidiConsumer.h"
|
|
|
|
|
#include "MidiProducer.h"
|
|
|
|
|
#include "MidiRoster.h"
|
|
|
|
|
#include "MidiRosterLooper.h"
|
|
|
|
|
#include "protocol.h"
|
|
|
|
|
|
2004-05-13 10:55:51 +00:00
|
|
|
using namespace BPrivate;
|
|
|
|
|
|
2002-12-31 14:34:17 +00:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
BMidiRosterLooper::BMidiRosterLooper()
|
2003-03-02 12:23:20 +00:00
|
|
|
: BLooper("MidiRosterLooper")
|
2002-12-31 14:34:17 +00:00
|
|
|
{
|
|
|
|
|
initLock = -1;
|
|
|
|
|
roster = NULL;
|
|
|
|
|
watcher = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
BMidiRosterLooper::~BMidiRosterLooper()
|
|
|
|
|
{
|
|
|
|
|
StopWatching();
|
|
|
|
|
|
|
|
|
|
if (initLock >= B_OK)
|
|
|
|
|
{
|
|
|
|
|
delete_sem(initLock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At this point, our list may still contain endpoints with a
|
|
|
|
|
// zero reference count. These objects are proxies for remote
|
|
|
|
|
// endpoints, so we can safely delete them. If the list also
|
|
|
|
|
// contains endpoints with a non-zero refcount (which can be
|
|
|
|
|
// either remote or local), we will output a warning message.
|
|
|
|
|
// It would have been better to jump into the debugger, but I
|
|
|
|
|
// did not want to risk breaking any (misbehaving) old apps.
|
|
|
|
|
|
|
|
|
|
for (int32 t = 0; t < CountEndpoints(); ++t)
|
|
|
|
|
{
|
|
|
|
|
BMidiEndpoint* endp = EndpointAt(t);
|
|
|
|
|
if (endp->refCount > 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf(
|
|
|
|
|
stderr, "[midi] WARNING: Endpoint %ld (%p) has "
|
|
|
|
|
"not been Release()d properly (refcount = %ld)\n",
|
|
|
|
|
endp->ID(), endp, endp->refCount);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
delete endp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool BMidiRosterLooper::Init(BMidiRoster* roster_)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(roster_ != NULL)
|
|
|
|
|
|
|
|
|
|
roster = roster_;
|
|
|
|
|
|
|
|
|
|
// We create a semaphore with a zero count. BMidiRoster's
|
|
|
|
|
// MidiRoster() method will try to acquire this semaphore,
|
|
|
|
|
// but blocks because the count is 0. When we receive the
|
|
|
|
|
// "app registered" message in our MessageReceived() hook,
|
|
|
|
|
// we release the semaphore and MidiRoster() will unblock.
|
|
|
|
|
|
2003-03-02 12:23:20 +00:00
|
|
|
initLock = create_sem(0, "InitLock");
|
2002-12-31 14:34:17 +00:00
|
|
|
|
|
|
|
|
if (initLock < 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(!endpoints.HasItem(endp))
|
|
|
|
|
|
|
|
|
|
// We store the endpoints sorted by ID, because that
|
|
|
|
|
// simplifies the implementation of NextEndpoint().
|
|
|
|
|
// Although the midi_server assigns IDs in ascending
|
|
|
|
|
// order, we can't assume that the mNEW messages also
|
|
|
|
|
// are delivered in this order (mostly they will be).
|
|
|
|
|
|
|
|
|
|
int32 t;
|
|
|
|
|
for (t = CountEndpoints(); t > 0; --t)
|
|
|
|
|
{
|
|
|
|
|
BMidiEndpoint* other = EndpointAt(t - 1);
|
|
|
|
|
if (endp->ID() > other->ID())
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
endpoints.AddItem(endp, t);
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
DumpEndpoints();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void BMidiRosterLooper::RemoveEndpoint(BMidiEndpoint* endp)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(endp != NULL)
|
|
|
|
|
ASSERT(endpoints.HasItem(endp))
|
|
|
|
|
|
|
|
|
|
endpoints.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();
|
|
|
|
|
watcher = new BMessenger(*watcher_);
|
|
|
|
|
|
|
|
|
|
AllEndpoints();
|
|
|
|
|
AllConnections();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void BMidiRosterLooper::StopWatching()
|
|
|
|
|
{
|
|
|
|
|
delete watcher;
|
|
|
|
|
watcher = 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(initLock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
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->name = name;
|
|
|
|
|
cons->id = id;
|
|
|
|
|
cons->isRegistered = isRegistered;
|
|
|
|
|
cons->port = port;
|
|
|
|
|
cons->latency = latency;
|
|
|
|
|
*(cons->properties) = properties;
|
|
|
|
|
AddEndpoint(cons);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // producer
|
|
|
|
|
{
|
|
|
|
|
BMidiProducer* prod = new BMidiProducer();
|
|
|
|
|
prod->name = name;
|
|
|
|
|
prod->id = id;
|
|
|
|
|
prod->isRegistered = isRegistered;
|
|
|
|
|
*(prod->properties) = 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 the client is watching, and the endpoint is
|
|
|
|
|
// registered remote, we need to let it know that
|
|
|
|
|
// the endpoint is now unregistered.
|
|
|
|
|
|
|
|
|
|
if (endp->IsRemote() && endp->IsRegistered())
|
|
|
|
|
{
|
|
|
|
|
if (watcher != NULL)
|
|
|
|
|
{
|
|
|
|
|
BMessage notify;
|
|
|
|
|
notify.AddInt32("be:op", B_MIDI_UNREGISTERED);
|
|
|
|
|
ChangeEvent(¬ify, endp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the proxy object for this endpoint is no
|
|
|
|
|
// longer being used, we can delete it. However,
|
|
|
|
|
// if the refcount is not zero, we must defer
|
|
|
|
|
// destruction until the client Release()'s the
|
|
|
|
|
// object. We clear the "isRegistered" flag to
|
|
|
|
|
// let the client know the object is now invalid.
|
|
|
|
|
|
|
|
|
|
if (endp->refCount == 0)
|
|
|
|
|
{
|
|
|
|
|
delete endp;
|
|
|
|
|
}
|
|
|
|
|
else // still being used
|
|
|
|
|
{
|
|
|
|
|
endp->isRegistered = false;
|
|
|
|
|
endp->isAlive = 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 (watcher != 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->isRegistered != isRegistered)
|
|
|
|
|
{
|
|
|
|
|
endp->isRegistered = isRegistered;
|
|
|
|
|
|
|
|
|
|
if (watcher != 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->name != name)
|
|
|
|
|
{
|
|
|
|
|
endp->name = name;
|
|
|
|
|
|
|
|
|
|
if ((watcher != 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->properties) = properties;
|
|
|
|
|
|
|
|
|
|
if ((watcher != 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->latency != latency)
|
|
|
|
|
{
|
|
|
|
|
cons->latency = latency;
|
|
|
|
|
|
|
|
|
|
if ((watcher != 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(watcher != 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");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
watcher->SendMessage(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void BMidiRosterLooper::ConnectionEvent(
|
|
|
|
|
BMidiProducer* prod, BMidiConsumer* cons, bool mustConnect)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(watcher != 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
watcher->SendMessage(¬ify);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void BMidiRosterLooper::DisconnectDeadConsumer(BMidiConsumer* cons)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(cons != NULL)
|
|
|
|
|
|
|
|
|
|
// Note: Rather than looping through each producer's list
|
|
|
|
|
// of connected consumers, we let ConnectionBroken() tell
|
|
|
|
|
// us whether the consumer really was connected.
|
|
|
|
|
|
|
|
|
|
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() && (watcher != NULL))
|
|
|
|
|
{
|
|
|
|
|
ConnectionEvent(prod, cons, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void BMidiRosterLooper::DisconnectDeadProducer(BMidiProducer* prod)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(prod != NULL)
|
|
|
|
|
|
|
|
|
|
// We don't need to lock or remove the consumers from
|
|
|
|
|
// the producer's list of connections, because when this
|
|
|
|
|
// function is called, we're destroying the object.
|
|
|
|
|
|
|
|
|
|
if (prod->IsRemote() && (watcher != NULL))
|
|
|
|
|
{
|
|
|
|
|
for (int32 t = 0; t < prod->CountConsumers(); ++t)
|
|
|
|
|
{
|
|
|
|
|
ConnectionEvent(prod, prod->ConsumerAt(t), false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
int32 BMidiRosterLooper::CountEndpoints()
|
|
|
|
|
{
|
|
|
|
|
return endpoints.CountItems();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
BMidiEndpoint* BMidiRosterLooper::EndpointAt(int32 index)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(index >= 0 && index < CountEndpoints())
|
|
|
|
|
|
|
|
|
|
return (BMidiEndpoint*) endpoints.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 %ld (%p):\n", t, endp);
|
|
|
|
|
|
|
|
|
|
printf(
|
|
|
|
|
"\t\tid %ld, name '%s', %s, %s, %s, %s, refcount %ld\n",
|
|
|
|
|
endp->ID(), endp->Name(),
|
|
|
|
|
endp->IsConsumer() ? "consumer" : "producer",
|
|
|
|
|
endp->IsRegistered() ? "registered" : "unregistered",
|
|
|
|
|
endp->IsLocal() ? "local" : "remote",
|
|
|
|
|
endp->IsValid() ? "valid" : "invalid", endp->refCount);
|
|
|
|
|
|
|
|
|
|
printf("\t\tproperties: ");
|
|
|
|
|
endp->properties->PrintToStream();
|
|
|
|
|
|
|
|
|
|
if (endp->IsConsumer())
|
|
|
|
|
{
|
|
|
|
|
BMidiConsumer* cons = (BMidiConsumer*) endp;
|
|
|
|
|
printf("\t\tport %ld, latency %Ld\n",
|
|
|
|
|
cons->port, cons->latency);
|
|
|
|
|
}
|
|
|
|
|
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 %ld (%p)\n", cons->ID(), cons);
|
|
|
|
|
}
|
|
|
|
|
prod->UnlockProducer();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("*** END DumpEndpoints\n");
|
|
|
|
|
Unlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|