Checkin of the source code for midi2 kit milestone 1.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2337 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
mahlzeit
2002-12-31 14:34:17 +00:00
parent 3569d43900
commit 53284a9fdb
13 changed files with 2664 additions and 338 deletions
+5 -5
View File
@@ -5,15 +5,15 @@ UsePrivateHeaders midi ;
UsePublicHeaders midi2 ;
SharedLibrary midi2 :
MidiRoster.cpp
MidiEndpoint.cpp
MidiConsumer.cpp
MidiEndpoint.cpp
MidiLocalConsumer.cpp
MidiProducer.cpp
MidiLocalProducer.cpp
MidiProducer.cpp
MidiRoster.cpp
MidiRosterLooper.cpp
;
LinkSharedOSLibs libmidi2.so :
be root
be
;
+16 -7
View File
@@ -1,19 +1,28 @@
/**
* @file MidiConsumer.cpp
*
* Implementation of the BMidiConsumer class.
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#include "debug.h"
#include "MidiConsumer.h"
#include "MidiEndpoint.h"
#include "protocol.h"
//------------------------------------------------------------------------------
bigtime_t BMidiConsumer::Latency() const
{
return fLatency;
bigtime_t res = 0LL;
if (LockLooper())
{
res = latency;
UnlockLooper();
}
return res;
}
//------------------------------------------------------------------------------
@@ -21,15 +30,16 @@ bigtime_t BMidiConsumer::Latency() const
BMidiConsumer::BMidiConsumer(const char* name)
: BMidiEndpoint(name)
{
fLatency = 0;
isConsumer = true;
latency = 0LL;
port = 0;
}
//------------------------------------------------------------------------------
BMidiConsumer::~BMidiConsumer()
{
if (fEventPort != B_NO_MORE_PORTS)
delete_port(fEventPort);
// Do nothing.
}
//------------------------------------------------------------------------------
@@ -44,4 +54,3 @@ void BMidiConsumer::_Reserved7() { }
void BMidiConsumer::_Reserved8() { }
//------------------------------------------------------------------------------
+256 -42
View File
@@ -1,73 +1,110 @@
/**
* @file MidiEndpoint.cpp
*
* Implementation of the BMidiEndpoint class.
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#include <MidiRoster.h>
#include "debug.h"
#include "MidiEndpoint.h"
#include "MidiRoster.h"
#include "MidiRosterLooper.h"
#include "protocol.h"
//------------------------------------------------------------------------------
const char* BMidiEndpoint::Name() const
{
return fName.String();
const char* str = NULL;
// It seems reasonable to assume that the pointer
// returned by BString::String() can change when the
// string is modified, e.g. to allocate more space.
// That's why we need to lock here too.
if (LockLooper())
{
str = name.String();
UnlockLooper();
}
return str;
}
//------------------------------------------------------------------------------
void BMidiEndpoint::SetName(const char* name)
void BMidiEndpoint::SetName(const char* name_)
{
BMidiRoster *roster = BMidiRoster::MidiRoster();
roster->Rename(this, name);
fName.SetTo(name);
if (name_ == NULL)
{
WARN("SetName() does not accept a NULL name");
return;
}
else if (IsRemote())
{
WARN("SetName() is not allowed on remote endpoints");
return;
}
else if (!IsValid())
{
return;
}
else if (name != name_)
{
BMessage msg;
msg.AddString("midi:name", name_);
if (SendChangeRequest(&msg) == B_OK)
{
if (LockLooper())
{
name.SetTo(name_);
UnlockLooper();
}
}
}
}
//------------------------------------------------------------------------------
int32 BMidiEndpoint::ID() const
{
return fID;
return id;
}
//------------------------------------------------------------------------------
bool BMidiEndpoint::IsProducer() const
{
return (fFlags && 0x01) == 0x01;
return !isConsumer;
}
//------------------------------------------------------------------------------
bool BMidiEndpoint::IsConsumer() const
{
return (fFlags && 0x01) == 0x01;
return isConsumer;
}
//------------------------------------------------------------------------------
bool BMidiEndpoint::IsRemote() const
{
UNIMPLEMENTED
return false;
return !isLocal;
}
//------------------------------------------------------------------------------
bool BMidiEndpoint::IsLocal() const
{
UNIMPLEMENTED
return false;
return isLocal;
}
//------------------------------------------------------------------------------
bool BMidiEndpoint::IsPersistent() const
{
UNIMPLEMENTED
return false;
}
@@ -75,82 +112,194 @@ bool BMidiEndpoint::IsPersistent() const
bool BMidiEndpoint::IsValid() const
{
UNIMPLEMENTED
return false;
if (IsLocal())
{
return (ID() > 0);
}
else // remote endpoint
{
return IsRegistered();
}
}
//------------------------------------------------------------------------------
status_t BMidiEndpoint::Release()
{
if (1 == atomic_add(&fRefCount, -1))
int32 old = atomic_add(&refCount, -1);
TRACE(("BMidiEndpoint::Release refCount is now %ld", old - 1))
if (old == 1)
{
Unregister();
delete this;
return B_OK;
// If the reference count of a local endpoint drops to zero,
// we must delete it. The destructor of BMidiLocalXXX calls
// BMidiRoster::DeleteLocal(), which does all the hard work.
// If we are a proxy for a remote endpoint, we must only be
// deleted if that remote endpoint no longer exists.
if (IsLocal() || !isAlive)
{
delete this;
}
}
else
else if (old <= 0)
{
return B_ERROR;
debugger("too many calls to Release()");
}
return B_OK;
}
//------------------------------------------------------------------------------
status_t BMidiEndpoint::Acquire()
{
atomic_add(&fRefCount, 1);
int32 old = atomic_add(&refCount, 1);
TRACE(("BMidiEndpoint::Acquire refCount is now %ld", old + 1))
return B_OK;
}
//------------------------------------------------------------------------------
status_t BMidiEndpoint::SetProperties(const BMessage* props)
status_t BMidiEndpoint::SetProperties(const BMessage* properties_)
{
UNIMPLEMENTED
return B_ERROR;
if (properties_ == NULL)
{
WARN("SetProperties() does not accept a NULL message")
return B_BAD_VALUE;
}
else if (IsRemote())
{
WARN("SetProperties() is not allowed on remote endpoints");
return B_ERROR;
}
else if (!IsValid())
{
return B_ERROR;
}
else
{
BMessage msg;
msg.AddMessage("midi:properties", properties_);
status_t err = SendChangeRequest(&msg);
if (err == B_OK)
{
if (LockLooper())
{
*properties = *properties_;
UnlockLooper();
}
}
return err;
}
}
//------------------------------------------------------------------------------
status_t BMidiEndpoint::GetProperties(BMessage* props) const
status_t BMidiEndpoint::GetProperties(BMessage* properties_) const
{
UNIMPLEMENTED
return B_ERROR;
if (properties_ == NULL)
{
WARN("GetProperties() does not accept NULL properties")
return B_BAD_VALUE;
}
if (LockLooper())
{
*properties_ = *properties;
UnlockLooper();
}
return B_OK;
}
//------------------------------------------------------------------------------
status_t BMidiEndpoint::Register(void)
{
UNIMPLEMENTED
return B_ERROR;
if (IsRemote())
{
WARN("You cannot Register() remote endpoints");
return B_ERROR;
}
else if (IsRegistered())
{
WARN("This endpoint is already registered");
return B_OK;
}
else if (!IsValid())
{
return B_ERROR;
}
else
{
return SendRegisterRequest(true);
}
}
//------------------------------------------------------------------------------
status_t BMidiEndpoint::Unregister(void)
{
UNIMPLEMENTED
return B_ERROR;
if (IsRemote())
{
WARN("You cannot Unregister() remote endpoints");
return B_ERROR;
}
else if (!IsRegistered())
{
WARN("This endpoint is already unregistered");
return B_OK;
}
else if (!IsValid())
{
return B_ERROR;
}
else
{
return SendRegisterRequest(false);
}
}
//------------------------------------------------------------------------------
BMidiEndpoint::BMidiEndpoint(const char* name)
BMidiEndpoint::BMidiEndpoint(const char* name_)
{
fName = BString(name);
fStatus = B_OK;
fFlags = 0;
fRefCount = 0;
// fID = MidiRosterApp::GetNextFreeID();
TRACE(("BMidiEndpoint::BMidiEndpoint"))
if (name_ != NULL)
{
name.SetTo(name_);
}
id = 0;
refCount = 0;
isLocal = false;
isRegistered = false;
isAlive = true;
properties = new BMessage;
}
//------------------------------------------------------------------------------
BMidiEndpoint::~BMidiEndpoint()
{
UNIMPLEMENTED
TRACE(("BMidiEndpoint::~BMidiEndpoint (%p)", this))
if (refCount > 0)
{
debugger(
"you should use Release() to dispose of endpoints; "
"do not \"delete\" them or allocate them on the stack!");
}
delete properties;
}
//------------------------------------------------------------------------------
@@ -166,3 +315,68 @@ void BMidiEndpoint::_Reserved8() { }
//------------------------------------------------------------------------------
status_t BMidiEndpoint::SendRegisterRequest(bool registered)
{
BMessage msg;
msg.AddBool("midi:registered", registered);
status_t err = SendChangeRequest(&msg);
if (err == B_OK)
{
if (LockLooper())
{
isRegistered = registered;
UnlockLooper();
}
}
return err;
}
//------------------------------------------------------------------------------
status_t BMidiEndpoint::SendChangeRequest(BMessage* msg)
{
ASSERT(msg != NULL)
msg->what = MSG_CHANGE_ENDPOINT;
msg->AddInt32("midi:id", ID());
BMessage reply;
status_t err = BMidiRoster::MidiRoster()->SendRequest(msg, &reply);
if (err != B_OK) { return err; }
status_t res;
if (reply.FindInt32("midi:result", &res) == B_OK)
{
return res;
}
return B_ERROR;
}
//------------------------------------------------------------------------------
bool BMidiEndpoint::IsRegistered() const
{
// No need to protect this with a lock, because reading
// and writing a bool is always an atomic operation.
return isRegistered;
}
//------------------------------------------------------------------------------
bool BMidiEndpoint::LockLooper() const
{
return BMidiRoster::MidiRoster()->looper->Lock();
}
//------------------------------------------------------------------------------
void BMidiEndpoint::UnlockLooper() const
{
BMidiRoster::MidiRoster()->looper->Unlock();
}
//------------------------------------------------------------------------------
+40 -9
View File
@@ -1,48 +1,80 @@
/**
* @file MidiLocalConsumer.cpp
*
* Implementation of the BMidiLocalConsumer class.
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#include "debug.h"
#include "MidiConsumer.h"
#include "MidiRoster.h"
#include "protocol.h"
//------------------------------------------------------------------------------
BMidiLocalConsumer::BMidiLocalConsumer(const char* name)
: BMidiConsumer(name)
{
fFlags |= 0x10;
TRACE(("BMidiLocalConsumer::BMidiLocalConsumer"))
isLocal = true;
refCount = 1;
BMidiRoster::MidiRoster()->CreateLocal(this);
}
//------------------------------------------------------------------------------
BMidiLocalConsumer::~BMidiLocalConsumer()
{
UNIMPLEMENTED
TRACE(("BMidiLocalConsumer::~BMidiLocalConsumer"))
BMidiRoster::MidiRoster()->DeleteLocal(this);
}
//------------------------------------------------------------------------------
void BMidiLocalConsumer::SetLatency(bigtime_t latency)
void BMidiLocalConsumer::SetLatency(bigtime_t latency_)
{
fLatency = latency;
if (latency_ < 0)
{
WARN("SetLatency() does not accept negative values");
return;
}
else if (!IsValid())
{
return;
}
else if (latency != latency_)
{
BMessage msg;
msg.AddInt64("midi:latency", latency_);
if (SendChangeRequest(&msg) == B_OK)
{
if (LockLooper())
{
latency = latency_;
UnlockLooper();
}
}
}
}
//------------------------------------------------------------------------------
int32 BMidiLocalConsumer::GetProducerID(void)
{
return fCurrentProducer;
UNIMPLEMENTED
return 0;
}
//------------------------------------------------------------------------------
void BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data)
{
fTimeout = when;
fTimeoutData = data;
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
@@ -165,4 +197,3 @@ void BMidiLocalConsumer::_Reserved7() { }
void BMidiLocalConsumer::_Reserved8() { }
//------------------------------------------------------------------------------
+24 -16
View File
@@ -1,40 +1,57 @@
/**
* @file MidiLocalProducer.cpp
*
* Implementation of the BMidiLocalProducer class.
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#include "debug.h"
#include "MidiConsumer.h"
#include "MidiProducer.h"
#include "MidiRoster.h"
#include "protocol.h"
//------------------------------------------------------------------------------
BMidiLocalProducer::BMidiLocalProducer(const char* name)
: BMidiProducer(name)
{
fFlags |= 0x10;
TRACE(("BMidiLocalProducer::BMidiLocalProducer"))
isLocal = true;
refCount = 1;
BMidiRoster::MidiRoster()->CreateLocal(this);
}
//------------------------------------------------------------------------------
BMidiLocalProducer::~BMidiLocalProducer()
{
UNIMPLEMENTED
TRACE(("BMidiLocalProducer::~BMidiLocalProducer"))
BMidiRoster::MidiRoster()->DeleteLocal(this);
}
//------------------------------------------------------------------------------
void BMidiLocalProducer::Connected(BMidiConsumer* dest)
void BMidiLocalProducer::Connected(BMidiConsumer* cons)
{
UNIMPLEMENTED
ASSERT(cons != NULL)
TRACE(("Connected() %ld to %ld", ID(), cons->ID()))
// Do nothing.
}
//------------------------------------------------------------------------------
void BMidiLocalProducer::Disconnected(BMidiConsumer* dest)
void BMidiLocalProducer::Disconnected(BMidiConsumer* cons)
{
UNIMPLEMENTED
ASSERT(cons != NULL)
TRACE(("Disconnected() %ld from %ld", ID(), cons->ID()))
// Do nothing.
}
//------------------------------------------------------------------------------
@@ -136,14 +153,6 @@ void BMidiLocalProducer::SprayTempoChange(
//------------------------------------------------------------------------------
void BMidiLocalProducer::SprayEvent(
BMidiEvent* event, size_t length) const
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
void BMidiLocalProducer::_Reserved1() { }
void BMidiLocalProducer::_Reserved2() { }
void BMidiLocalProducer::_Reserved3() { }
@@ -154,4 +163,3 @@ void BMidiLocalProducer::_Reserved7() { }
void BMidiLocalProducer::_Reserved8() { }
//------------------------------------------------------------------------------
+184 -35
View File
@@ -1,64 +1,93 @@
/**
* @file MidiProducer.cpp
*
* Implementation of the BMidiProducer class.
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#include "debug.h"
#include "MidiConsumer.h"
#include "MidiProducer.h"
#include "MidiEndpoint.h"
#include "MidiRoster.h"
#include "MidiRosterLooper.h"
#include "protocol.h"
//------------------------------------------------------------------------------
status_t BMidiProducer::Connect(BMidiConsumer* toObject)
status_t BMidiProducer::Connect(BMidiConsumer* cons)
{
/*
if (toObject != NULL)
if (cons == NULL)
{
if (fConnections->Add(toObject) == true)
{
fConnectionCount++;
}
WARN("Connect() does not accept a NULL consumer")
return B_BAD_VALUE;
}
else if (!IsValid() || !cons->IsValid())
{
return B_ERROR;
}
else
{
return SendConnectRequest(cons, true);
}
BMidiRoster *roster = BMidiRoster::MidiRoster();
return roster->Connect(this, toObject);
*/
return B_ERROR;
}
//------------------------------------------------------------------------------
status_t BMidiProducer::Disconnect(BMidiConsumer* toObject)
status_t BMidiProducer::Disconnect(BMidiConsumer* cons)
{
/*
if (toObject != NULL)
if (cons == NULL)
{
if (fConnections->Remove(toObject) == true)
{
fConnectionCount--;
BMidiRoster *roster = BMidiRoster::MidiRoster();
return roster->Disconnect(this, toObject);
}
WARN("Disconnect() does not accept a NULL consumer")
return B_BAD_VALUE;
}
else if (!IsValid() || !cons->IsValid())
{
return B_ERROR;
}
else
{
return SendConnectRequest(cons, false);
}
*/
return B_ERROR;
}
//------------------------------------------------------------------------------
bool BMidiProducer::IsConnected(BMidiConsumer* toObject) const
bool BMidiProducer::IsConnected(BMidiConsumer* cons) const
{
// return fConnections->IsIn(toObject);
return false;
bool isConnected = false;
if (cons != NULL)
{
if (LockProducer())
{
isConnected = connections->HasItem(cons);
UnlockProducer();
}
}
return isConnected;
}
//------------------------------------------------------------------------------
BList* BMidiProducer::Connections() const
{
// return fConnections;
return NULL;
BList* list = new BList();
if (LockProducer())
{
for (int32 t = 0; t < CountConsumers(); ++t)
{
BMidiConsumer* cons = ConsumerAt(t);
cons->Acquire();
list->AddItem(cons);
}
UnlockProducer();
}
return list;
}
//------------------------------------------------------------------------------
@@ -66,18 +95,17 @@ BList* BMidiProducer::Connections() const
BMidiProducer::BMidiProducer(const char* name)
: BMidiEndpoint(name)
{
/*
fConnections = new BMidiList();
fConnectionCount = 1;
fLock = BLocker("BMidiProducer Lock");
*/
isConsumer = false;
connections = new BList;
locker = new BLocker();
}
//------------------------------------------------------------------------------
BMidiProducer::~BMidiProducer()
{
UNIMPLEMENTED
delete connections;
delete locker;
}
//------------------------------------------------------------------------------
@@ -93,3 +121,124 @@ void BMidiProducer::_Reserved8() { }
//------------------------------------------------------------------------------
status_t BMidiProducer::SendConnectRequest(
BMidiConsumer* cons, bool mustConnect)
{
ASSERT(cons != NULL)
BMessage msg, reply;
if (mustConnect)
{
msg.what = MSG_CONNECT_ENDPOINTS;
}
else
{
msg.what = MSG_DISCONNECT_ENDPOINTS;
}
msg.AddInt32("midi:producer", ID());
msg.AddInt32("midi:consumer", cons->ID());
status_t err = BMidiRoster::MidiRoster()->SendRequest(&msg, &reply);
if (err != B_OK) { return err; }
status_t res;
if (reply.FindInt32("midi:result", &res) == B_OK)
{
if (res == B_OK)
{
if (mustConnect)
{
ConnectionMade(cons);
}
else
{
ConnectionBroken(cons);
}
#ifdef DEBUG
BMidiRoster::MidiRoster()->looper->DumpEndpoints();
#endif
}
return res;
}
return B_ERROR;
}
//------------------------------------------------------------------------------
void BMidiProducer::ConnectionMade(BMidiConsumer* cons)
{
ASSERT(cons != NULL)
if (LockProducer())
{
ASSERT(!connections->HasItem(cons))
connections->AddItem(cons);
UnlockProducer();
}
if (IsLocal())
{
((BMidiLocalProducer*) this)->Connected(cons);
}
}
//------------------------------------------------------------------------------
bool BMidiProducer::ConnectionBroken(BMidiConsumer* cons)
{
ASSERT(cons != NULL)
bool wasConnected = false;
if (LockProducer())
{
wasConnected = connections->RemoveItem(cons);
UnlockProducer();
}
if (wasConnected && IsLocal())
{
((BMidiLocalProducer*) this)->Disconnected(cons);
}
return wasConnected;
}
//------------------------------------------------------------------------------
int32 BMidiProducer::CountConsumers() const
{
return connections->CountItems();
}
//------------------------------------------------------------------------------
BMidiConsumer* BMidiProducer::ConsumerAt(int32 index) const
{
ASSERT(connections != NULL)
ASSERT(index >= 0 && index < CountConsumers())
return (BMidiConsumer*) connections->ItemAt(index);
}
//------------------------------------------------------------------------------
bool BMidiProducer::LockProducer() const
{
return locker->Lock();
}
//------------------------------------------------------------------------------
void BMidiProducer::UnlockProducer() const
{
locker->Unlock();
}
//------------------------------------------------------------------------------
+311 -186
View File
@@ -1,26 +1,90 @@
/**
* @file MidiRoster.cpp
*
* Implementation of the BMidiRoster class.
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#include "debug.h"
#include "MidiConsumer.h"
#include "MidiRoster.h"
#include "MidiRosterLooper.h"
#include "protocol.h"
// The midi_debug_level and midi_dispatcher_priority symbols
// were exported by Be's libmidi2, and even though they do not
// appear in the headers, some apps may still be using them.
// For backwards compatibility's sake, we export those symbols
// as well, even though we do not use them for anything.
/** Not used. For backwards compatibility only. */
int32 midi_debug_level = 0;
/** Not used. For backwards compatibility only. */
int32 midi_dispatcher_priority = B_REAL_TIME_PRIORITY;
//------------------------------------------------------------------------------
/**
* The one and only BMidiRoster instance, which is created
* the first time the client app calls MidiRoster(). It is
* destroyed by the BMidiRosterKiller when the app quits.
*/
static BMidiRoster* roster = NULL;
/** Destroys the BMidiRoster instance when the app quits. */
static class BMidiRosterKiller
{
public:
~BMidiRosterKiller()
{
delete roster;
}
}
killer;
//------------------------------------------------------------------------------
BMidiEndpoint* BMidiRoster::NextEndpoint(int32* id)
{
UNIMPLEMENTED
return NULL;
}
BMidiEndpoint* endp = NULL;
if (id != NULL)
{
BMidiRosterLooper* looper = MidiRoster()->looper;
if (looper->Lock())
{
endp = looper->NextEndpoint(id);
if (endp != NULL)
{
endp->Acquire();
}
looper->Unlock();
}
}
return endp;
}
//------------------------------------------------------------------------------
BMidiProducer* BMidiRoster::NextProducer(int32* id)
{
UNIMPLEMENTED
BMidiEndpoint* endp;
while ((endp = NextEndpoint(id)) != NULL)
{
if (endp->IsProducer())
{
return (BMidiProducer*) endp;
}
else
{
endp->Release();
}
}
return NULL;
}
@@ -28,84 +92,200 @@ BMidiProducer* BMidiRoster::NextProducer(int32* id)
BMidiConsumer* BMidiRoster::NextConsumer(int32* id)
{
UNIMPLEMENTED
BMidiEndpoint* endp;
while ((endp = NextEndpoint(id)) != NULL)
{
if (endp->IsConsumer())
{
return (BMidiConsumer*) endp;
}
else
{
endp->Release();
}
}
return NULL;
}
//------------------------------------------------------------------------------
BMidiEndpoint* BMidiRoster::FindEndpoint(int32 id, bool local_only)
BMidiEndpoint* BMidiRoster::FindEndpoint(int32 id, bool localOnly)
{
UNIMPLEMENTED
return NULL;
BMidiEndpoint* endp = NULL;
BMidiRosterLooper* looper = MidiRoster()->looper;
if (looper->Lock())
{
endp = looper->FindEndpoint(id);
if ((endp != NULL) && endp->IsRemote())
{
if (localOnly || !endp->IsRegistered())
{
endp = NULL;
}
}
if (endp != NULL)
{
endp->Acquire();
}
looper->Unlock();
}
return endp;
}
//------------------------------------------------------------------------------
BMidiProducer* BMidiRoster::FindProducer(int32 id, bool localOnly)
{
BMidiEndpoint* endp = FindEndpoint(id, localOnly);
if ((endp != NULL) && !endp->IsProducer())
{
endp->Release();
endp = NULL;
}
return (BMidiProducer*) endp;
}
//------------------------------------------------------------------------------
BMidiProducer* BMidiRoster::FindProducer(int32 id, bool local_only)
BMidiConsumer* BMidiRoster::FindConsumer(int32 id, bool localOnly)
{
UNIMPLEMENTED
return NULL;
}
BMidiEndpoint* endp = FindEndpoint(id, localOnly);
//------------------------------------------------------------------------------
if ((endp != NULL) && !endp->IsConsumer())
{
endp->Release();
endp = NULL;
}
BMidiConsumer* BMidiRoster::FindConsumer(int32 id, bool local_only)
{
UNIMPLEMENTED
return NULL;
return (BMidiConsumer*) endp;
}
//------------------------------------------------------------------------------
void BMidiRoster::StartWatching(const BMessenger* msngr)
{
UNIMPLEMENTED
if (msngr == NULL)
{
WARN("StartWatching does not accept a NULL messenger")
}
else
{
BMidiRosterLooper* looper = MidiRoster()->looper;
if (looper->Lock())
{
looper->StartWatching(msngr);
looper->Unlock();
}
}
}
//------------------------------------------------------------------------------
void BMidiRoster::StopWatching()
{
UNIMPLEMENTED
BMidiRosterLooper* looper = MidiRoster()->looper;
if (looper->Lock())
{
looper->StopWatching();
looper->Unlock();
}
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Register(BMidiEndpoint* object)
status_t BMidiRoster::Register(BMidiEndpoint* endp)
{
UNIMPLEMENTED
return B_ERROR;
if (endp != NULL)
{
return endp->Register();
}
return B_BAD_VALUE;
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Unregister(BMidiEndpoint* object)
status_t BMidiRoster::Unregister(BMidiEndpoint* endp)
{
UNIMPLEMENTED
return B_ERROR;
if (endp != NULL)
{
return endp->Unregister();
}
return B_BAD_VALUE;
}
//------------------------------------------------------------------------------
BMidiRoster* BMidiRoster::MidiRoster()
{
UNIMPLEMENTED
return NULL;
if (roster == NULL)
{
new BMidiRoster();
}
return roster;
}
//------------------------------------------------------------------------------
BMidiRoster::BMidiRoster(BMessenger* remote)
BMidiRoster::BMidiRoster()
{
UNIMPLEMENTED
TRACE(("BMidiRoster::BMidiRoster"))
// While our constructor is executing, some function may
// call MidiRoster() again, which causes an endless loop.
// To prevent this, we immediately fill in "roster"; now
// subsequent calls to MidiRoster() won't mess up things.
roster = this;
server = new BMessenger(MIDI_SERVER_SIGNATURE);
looper = new BMidiRosterLooper();
if (!looper->Init(this)) { return; }
BMessage msg;
msg.what = MSG_REGISTER_APP;
msg.AddMessenger("midi:messenger", BMessenger(looper));
if (server->SendMessage(&msg, looper, TIMEOUT) != B_OK)
{
WARN("Cannot send request to midi_server");
return;
}
// Although unlikely, we may receive the midi_server's
// "app registered" reply before we lock the semaphore.
// In that case, BMidiRosterLooper's MessageReceived()
// will bump the semaphore count, and our acquire_sem()
// can grab the semaphore safely (without blocking).
acquire_sem(looper->initLock);
}
//------------------------------------------------------------------------------
BMidiRoster::~BMidiRoster()
{
UNIMPLEMENTED
TRACE(("BMidiRoster::~BMidiRoster"))
if (looper != NULL)
{
looper->Lock();
looper->Quit();
}
delete server;
}
//------------------------------------------------------------------------------
@@ -121,172 +301,117 @@ void BMidiRoster::_Reserved8() { }
//------------------------------------------------------------------------------
status_t BMidiRoster::RemoteConnect(
int32 producer, int32 consumer, int32 port)
void BMidiRoster::CreateLocal(BMidiEndpoint* endp)
{
UNIMPLEMENTED
return B_ERROR;
ASSERT(endp != NULL)
// We are being called from the BMidiLocalConsumer or
// BMidiLocalProducer constructor, so there is no need
// to lock anything, because at this point there cannot
// be multiple threads accessing the endpoint's data.
BMessage msg;
msg.what = MSG_CREATE_ENDPOINT;
msg.AddBool("midi:consumer", endp->isConsumer);
msg.AddBool("midi:registered", endp->isRegistered);
msg.AddString("midi:name", endp->Name());
msg.AddMessage("midi:properties", endp->properties);
if (endp->IsConsumer())
{
BMidiConsumer* consumer = (BMidiConsumer*) endp;
msg.AddInt32("midi:port", consumer->port);
msg.AddInt64("midi:latency", consumer->latency);
}
BMessage reply;
if (SendRequest(&msg, &reply) == B_OK)
{
status_t res;
if (reply.FindInt32("midi:result", &res) == B_OK)
{
if (res == B_OK)
{
int32 id;
if (reply.FindInt32("midi:id", &id) == B_OK)
{
endp->id = id;
if (looper->Lock())
{
looper->AddEndpoint(endp);
looper->Unlock();
}
}
}
}
}
// There are many things that can go wrong when creating
// a new endpoint, but BMidiEndpoint has no InitCheck()
// method to check for this. (You can, however, see if the
// endpoint's ID is 0 after the constructor returns, or
// call IsValid().) In any case, you should still Release()
// the endpoint to delete the object. (This is different
// from Be's implementation, which bumps the refcount only
// when creation succeeded. If you call Release(), you'll
// trip an assertion, so you can't delete these endpoints.)
}
//------------------------------------------------------------------------------
status_t BMidiRoster::RemoteDisconnect(int32 producer, int32 consumer)
void BMidiRoster::DeleteLocal(BMidiEndpoint* endp)
{
UNIMPLEMENTED
return B_ERROR;
ASSERT(endp != NULL)
BMessage msg;
msg.what = MSG_DELETE_ENDPOINT;
msg.AddInt32("midi:id", endp->ID());
// Note: this is always called from BMidiLocalConsumer's
// or BMidiLocalProducer's destructor, so we don't expect
// a reply from the server. If something went wrong, the
// object will be destroyed regardless.
server->SendMessage(&msg, (BHandler*) NULL, TIMEOUT);
// If the endpoint was successfully created, we must remove
// it from the list of endpoints. If creation failed, then
// we didn't put the endpoint on that list. If the endpoint
// was connected to anything, we must also disconnect it.
if (endp->ID() > 0)
{
if (looper->Lock())
{
looper->RemoveEndpoint(endp);
looper->Unlock();
}
}
}
//------------------------------------------------------------------------------
void BMidiRoster::RemoteConnected(int32 producer, int32 consumer)
status_t BMidiRoster::SendRequest(BMessage* msg, BMessage* reply)
{
UNIMPLEMENTED
ASSERT(msg != NULL)
ASSERT(reply != NULL)
status_t err = server->SendMessage(msg, reply, TIMEOUT, TIMEOUT);
if (err != B_OK)
{
WARN("Cannot send request to midi_server");
}
#ifdef DEBUG
if (err == B_OK)
{
printf("REPLY "); reply->PrintToStream();
}
#endif
return err;
}
//------------------------------------------------------------------------------
void BMidiRoster::RemoteDisconnected(int32 producer, int32 consumer)
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
BMidiEndpoint* BMidiRoster::RemoteCreateProducer(
int32 producer, const char* name)
{
UNIMPLEMENTED
return NULL;
}
//------------------------------------------------------------------------------
BMidiEndpoint* BMidiRoster::RemoteCreateConsumer(
int32 consumer, const char* name, int32 port, int32 latency)
{
UNIMPLEMENTED
return NULL;
}
//------------------------------------------------------------------------------
void BMidiRoster::RemoteDelete(BMidiEndpoint* object)
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
void BMidiRoster::RemoteRename(
BMidiEndpoint* object, const char* name)
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
void BMidiRoster::RemoteChangeLatency(
BMidiEndpoint* object, bigtime_t latency)
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Remote(int32 id, int32 op)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
status_t BMidiRoster::DoRemote(BMessage* msg, BMessage* result)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
void BMidiRoster::Rename(BMidiEndpoint* midi, const char* name)
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Release(BMidiEndpoint* midi)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Acquire(BMidiEndpoint* midi)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
void BMidiRoster::Create(BMidiEndpoint* midi)
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
void BMidiRoster::SetLatency(BMidiConsumer* midi, bigtime_t latency)
{
UNIMPLEMENTED
}
//------------------------------------------------------------------------------
status_t BMidiRoster::SetProperties(
BMidiEndpoint* midi, const BMessage* props)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
status_t BMidiRoster::GetProperties(
const BMidiEndpoint* midi, BMessage* props)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Connect(
BMidiProducer* source, BMidiConsumer* sink)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Disconnect(
BMidiProducer* source, BMidiConsumer* sink)
{
UNIMPLEMENTED
return B_ERROR;
}
//------------------------------------------------------------------------------
BLooper* BMidiRoster::Looper()
{
UNIMPLEMENTED
return NULL;
}
//------------------------------------------------------------------------------
+719
View File
@@ -0,0 +1,719 @@
/**
* @file MidiRosterLooper.cpp
*
* Implementation of the BMidiRosterLooper class.
*
* @author Matthijs Hollemans
*/
#include "debug.h"
#include "MidiConsumer.h"
#include "MidiProducer.h"
#include "MidiRoster.h"
#include "MidiRosterLooper.h"
#include "protocol.h"
//------------------------------------------------------------------------------
BMidiRosterLooper::BMidiRosterLooper()
{
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.
initLock = create_sem(0, NULL);
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(&notify, 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(&notify, 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(&notify, 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(&notify, 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(&notify, 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(&notify, 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(&notify);
}
//------------------------------------------------------------------------------
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
//------------------------------------------------------------------------------
+145
View File
@@ -0,0 +1,145 @@
/**
* @file MidiRosterLooper.h
*
* @author Matthijs Hollemans
*/
#ifndef MIDI_ROSTER_LOOPER_H
#define MIDI_ROSTER_LOOPER_H
#include <Looper.h>
#include <Message.h>
class BMidiRoster;
/**
* Receives messages from the midi_server on behalf of the
* BMidiRoster. Also keeps track of the list of endpoints.
*/
class BMidiRosterLooper : public BLooper
{
public:
BMidiRosterLooper();
virtual ~BMidiRosterLooper();
/** Starts up the looper. */
bool Init(BMidiRoster* roster);
/** Does the work for BMidiRoster::NextEndpoint(). */
BMidiEndpoint* NextEndpoint(int32* id);
/** Finds an endpoint in our list of endpoints. */
BMidiEndpoint* FindEndpoint(int32 id);
/** Adds an endpoint to our list. */
void AddEndpoint(BMidiEndpoint* endp);
/**
* Removes an endpoint from our list. Throws away
* any connections that this endpoint is part of.
*/
void RemoveEndpoint(BMidiEndpoint* endp);
/**
* Invoked when the client app wants to be kept informed
* about changes in the roster. From now on, we will send
* the messenger B_MIDI_EVENT notifications when something
* interesting happens. But first, we send a whole bunch
* of notifications about the registered remote endpoints,
* and the connections between them.
*/
void StartWatching(const BMessenger* watcher);
/**
* From now on, we will no longer send notifications to
* the client when something interesting happens.
*/
void StopWatching();
virtual void MessageReceived(BMessage* msg);
private:
friend class BMidiRoster;
friend class BMidiProducer;
typedef BLooper super;
void OnAppRegistered(BMessage* msg);
void OnEndpointCreated(BMessage* msg);
void OnEndpointDeleted(BMessage* msg);
void OnEndpointChanged(BMessage* msg);
void OnConnectedDisconnected(BMessage* msg);
void ChangeRegistered(BMessage* msg, BMidiEndpoint* endp);
void ChangeName(BMessage* msg, BMidiEndpoint* endp);
void ChangeProperties(BMessage* msg, BMidiEndpoint* endp);
void ChangeLatency(BMessage* msg, BMidiEndpoint* endp);
/**
* Removes the consumer from the list of connections of
* all the producers it is connected to. Also sends out
* B_MIDI_EVENT "disconnected" notifications if the
* consumer is remote and the client is watching.
*/
void DisconnectDeadConsumer(BMidiConsumer* cons);
/**
* Sends out B_MIDI_EVENT "disconnected" notifications
* if the producer is remote and the client is watching.
*/
void DisconnectDeadProducer(BMidiProducer* prod);
/**
* Sends B_MIDI_EVENT notifications for all registered
* remote endpoints to the watcher. Used when the client
* calls StartWatching().
*/
void AllEndpoints();
/**
* Sends B_MIDI_EVENT notifications for the connections
* between all registered remote endpoints to the watcher.
* Used when the client calls StartWatching().
*/
void AllConnections();
/**
* Sends a B_MIDI_EVENT notification to the watcher
* when another application changes the attributes
* of one of its endpoints.
*/
void ChangeEvent(BMessage* msg, BMidiEndpoint* endp);
/**
* Sends a B_MIDI_EVENT notification to the watcher
* when another application connects or disconnects
* the two endpoints.
*/
void ConnectionEvent(
BMidiProducer* prod, BMidiConsumer* cons, bool mustConnect);
int32 CountEndpoints();
BMidiEndpoint* EndpointAt(int32 index);
BMidiRoster* roster;
/**
* Makes sure BMidiRoster::MidiRoster() does not return
* until confirmation from the midi_server is received.
*/
sem_id initLock;
/** The object we send B_MIDI_EVENT notifications to. */
BMessenger* watcher;
/** All the endpoints in the system, local and remote. */
BList endpoints;
#ifdef DEBUG
void DumpEndpoints();
#endif
};
#endif // MIDI_ROSTER_LOOPER_H
-2
View File
@@ -8,6 +8,4 @@ Server midi_server :
LinkSharedOSLibs midi_server :
be
root
;
+764 -24
View File
@@ -1,27 +1,41 @@
/**
* @file MidiServerApp.cpp
*
* Implementation of the MidiServerApp class.
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#include <Alert.h>
#include "debug.h"
#include "MidiServerApp.h"
#include "ServerDefs.h"
#include "protocol.h"
//------------------------------------------------------------------------------
MidiServerApp::MidiServerApp()
: BApplication(MIDI_SERVER_SIGNATURE)
{
//roster = new BMidiRoster();
TRACE(("Running OpenBeOS MIDI server"))
nextId = 1;
}
//------------------------------------------------------------------------------
MidiServerApp::~MidiServerApp(void)
MidiServerApp::~MidiServerApp()
{
//delete endpoints;
for (int32 t = 0; t < CountApps(); ++t)
{
delete AppAt(t);
}
for (int32 t = 0; t < CountEndpoints(); ++t)
{
delete EndpointAt(t);
}
}
//------------------------------------------------------------------------------
@@ -38,45 +52,772 @@ void MidiServerApp::AboutRequested()
void MidiServerApp::MessageReceived(BMessage* msg)
{
#ifdef DEBUG
printf("IN "); msg->PrintToStream();
#endif
// About thread safety inside the midi_server: even though
// multiple apps may be sending requests to the server at
// the same time, the BLooper's thread handles passes them
// to our MessageReceived() one after the other, so there
// is no need to synchronize anything.
switch (msg->what)
{
case MSG_REGISTER_APP: OnRegisterApp(msg); break;
case MSG_CREATE_ENDPOINT: OnCreateEndpoint(msg); break;
case MSG_DELETE_ENDPOINT: OnDeleteEndpoint(msg); break;
case MSG_PURGE_ENDPOINT: OnPurgeEndpoint(msg); break;
case MSG_CHANGE_ENDPOINT: OnChangeEndpoint(msg); break;
case MSG_CONNECT_ENDPOINTS: OnConnectDisconnect(msg); break;
case MSG_DISCONNECT_ENDPOINTS: OnConnectDisconnect(msg); break;
default: super::MessageReceived(msg); break;
}
}
//------------------------------------------------------------------------------
/*int32 MidiServerApp::GetNextFreeID()
void MidiServerApp::OnRegisterApp(BMessage* msg)
{
return nextFreeID++;
}*/
TRACE(("MidiServerApp::OnRegisterApp"))
//------------------------------------------------------------------------------
// We only send the "app registered" message upon success,
// so if anything goes wrong here, we do not let the app
// know about it, and we consider it unregistered. (Most
// likely, the app is dead. If not, it freezes forever
// in anticipation of a message that will never arrive.)
/*BMidiEndpoint* MidiServerApp::NextEndPoint(int32* id)
{
int32 item = 0;
BMidiEndpoint *endpoint;
app_t* app = new app_t;
while ((endpoint = (BMidiEndpoint*) endpoints->ItemAt(item)) != NULL)
if (msg->FindMessenger("midi:messenger", &app->messenger) == B_OK)
{
if (endpoint->ID() > *id)
if (SendAllEndpoints(app))
{
endpoint->Acquire();
*id = endpoint->ID();
return endpoint;
if (SendAllConnections(app))
{
BMessage reply;
reply.what = MSG_APP_REGISTERED;
if (SendNotification(app, &reply))
{
apps.AddItem(app);
#ifdef DEBUG
DumpApps();
#endif
return;
}
}
}
item++;
}
return NULL;
}*/
delete app;
}
//------------------------------------------------------------------------------
/*BMidiRoster* MidiServerApp::GetRoster()
void MidiServerApp::OnCreateEndpoint(BMessage* msg)
{
return roster;
}*/
TRACE(("MidiServerApp::OnCreateEndpoint"))
status_t err;
endpoint_t* endp = new endpoint_t;
endp->app = WhichApp(msg);
if (endp->app == NULL)
{
err = B_ERROR;
}
else
{
err = B_BAD_VALUE;
if ((msg->FindBool("midi:consumer", &endp->consumer) == B_OK)
&& (msg->FindBool("midi:registered", &endp->registered) == B_OK)
&& (msg->FindString("midi:name", &endp->name) == B_OK)
&& (msg->FindMessage("midi:properties", &endp->properties) == B_OK))
{
if (endp->consumer)
{
if ((msg->FindInt32("midi:port", &endp->port) == B_OK)
&& (msg->FindInt64("midi:latency", &endp->latency) == B_OK))
{
err = B_OK;
}
}
else
{
err = B_OK;
}
}
}
BMessage reply;
if (err == B_OK)
{
endp->id = nextId++;
reply.AddInt32("midi:id", endp->id);
}
reply.AddInt32("midi:result", err);
if (SendReply(endp->app, msg, &reply) && (err == B_OK))
{
AddEndpoint(msg, endp);
}
else
{
delete endp;
}
}
//------------------------------------------------------------------------------
void MidiServerApp::OnDeleteEndpoint(BMessage* msg)
{
TRACE(("MidiServerApp::OnDeleteEndpoint"))
// Clients send the "delete endpoint" message from
// the BMidiEndpoint destructor, so there is no point
// sending a reply, because the endpoint object will
// be destroyed no matter what.
app_t* app = WhichApp(msg);
if (app != NULL)
{
endpoint_t* endp = WhichEndpoint(msg, app);
if (endp != NULL)
{
RemoveEndpoint(app, endp);
}
}
}
//------------------------------------------------------------------------------
void MidiServerApp::OnPurgeEndpoint(BMessage* msg)
{
TRACE(("MidiServerApp::OnPurgeEndpoint"))
// This performs the same task as OnDeleteEndpoint(),
// except that this message was send by the midi_server
// itself, so we don't check that the app that made the
// request really is the owner of the endpoint. (But we
// _do_ check that the message came from the server.)
if (!msg->IsSourceRemote())
{
int32 id;
if (msg->FindInt32("midi:id", &id) == B_OK)
{
endpoint_t* endp = FindEndpoint(id);
if (endp != NULL)
{
RemoveEndpoint(NULL, endp);
}
}
}
}
//------------------------------------------------------------------------------
void MidiServerApp::OnChangeEndpoint(BMessage* msg)
{
TRACE(("MidiServerApp::OnChangeEndpoint"))
endpoint_t* endp;
status_t err;
app_t* app = WhichApp(msg);
if (app == NULL)
{
err = B_ERROR;
}
else
{
endp = WhichEndpoint(msg, app);
if (endp == NULL)
{
err = B_BAD_VALUE;
}
else
{
err = B_OK;
}
}
BMessage reply;
reply.AddInt32("midi:result", err);
if (SendReply(app, msg, &reply) && (err == B_OK))
{
TRACE(("Endpoint %ld (%p) changed", endp->id, endp))
BMessage notify;
notify.what = MSG_ENDPOINT_CHANGED;
notify.AddInt32("midi:id", endp->id);
bool registered;
if (msg->FindBool("midi:registered", &registered) == B_OK)
{
notify.AddBool("midi:registered", registered);
endp->registered = registered;
}
BString name;
if (msg->FindString("midi:name", &name) == B_OK)
{
notify.AddString("midi:name", name);
endp->name = name;
}
BMessage properties;
if (msg->FindMessage("midi:properties", &properties) == B_OK)
{
notify.AddMessage("midi:properties", &properties);
endp->properties = properties;
}
bigtime_t latency;
if (msg->FindInt64("midi:latency", &latency) == B_OK)
{
notify.AddInt64("midi:latency", latency);
endp->latency = latency;
}
NotifyAll(&notify, app);
#ifdef DEBUG
DumpEndpoints();
#endif
}
}
//------------------------------------------------------------------------------
void MidiServerApp::OnConnectDisconnect(BMessage* msg)
{
TRACE(("MidiServerApp::OnConnectDisconnect"))
bool mustConnect = (msg->what == MSG_CONNECT_ENDPOINTS);
status_t err;
endpoint_t* prod;
endpoint_t* cons;
app_t* app = WhichApp(msg);
if (app == NULL)
{
err = B_ERROR;
}
else
{
err = B_BAD_VALUE;
int32 prodId, consId;
if ((msg->FindInt32("midi:producer", &prodId) == B_OK)
&& (msg->FindInt32("midi:consumer", &consId) == B_OK))
{
prod = FindEndpoint(prodId);
cons = FindEndpoint(consId);
if ((prod != NULL) && !prod->consumer)
{
if ((cons != NULL) && cons->consumer)
{
// It is an error to connect two endpoints that
// are already connected, or to disconnect two
// endpoints that are not connected at all.
if (mustConnect == prod->connections.HasItem(cons))
{
err = B_ERROR;
}
else
{
err = B_OK;
}
}
}
}
}
BMessage reply;
reply.AddInt32("midi:result", err);
if (SendReply(app, msg, &reply) && (err == B_OK))
{
if (mustConnect)
{
TRACE(("Connection made: %ld ---> %ld", prod->id, cons->id))
prod->connections.AddItem(cons);
}
else
{
TRACE(("Connection broken: %ld -X-> %ld", prod->id, cons->id))
prod->connections.RemoveItem(cons);
}
BMessage notify;
MakeConnectedNotification(&notify, prod, cons, mustConnect);
NotifyAll(&notify, app);
#ifdef DEBUG
DumpEndpoints();
#endif
}
}
//------------------------------------------------------------------------------
bool MidiServerApp::SendAllEndpoints(app_t* app)
{
ASSERT(app != NULL)
BMessage notify;
for (int32 t = 0; t < CountEndpoints(); ++t)
{
endpoint_t* endp = EndpointAt(t);
MakeCreatedNotification(&notify, endp);
if (!SendNotification(app, &notify)) { return false; }
}
return true;
}
//------------------------------------------------------------------------------
bool MidiServerApp::SendAllConnections(app_t* app)
{
ASSERT(app != NULL)
BMessage notify;
for (int32 t = 0; t < CountEndpoints(); ++t)
{
endpoint_t* prod = EndpointAt(t);
if (!prod->consumer)
{
for (int32 k = 0; k < CountConnections(prod); ++k)
{
endpoint_t* cons = ConnectionAt(prod, k);
MakeConnectedNotification(&notify, prod, cons, true);
if (!SendNotification(app, &notify)) { return false; }
}
}
}
return true;
}
//------------------------------------------------------------------------------
void MidiServerApp::AddEndpoint(BMessage* msg, endpoint_t* endp)
{
ASSERT(msg != NULL)
ASSERT(endp != NULL)
ASSERT(!endpoints.HasItem(endp))
TRACE(("Endpoint %ld (%p) added", endp->id, endp))
endpoints.AddItem(endp);
BMessage notify;
MakeCreatedNotification(&notify, endp);
NotifyAll(&notify, endp->app);
#ifdef DEBUG
DumpEndpoints();
#endif
}
//------------------------------------------------------------------------------
void MidiServerApp::RemoveEndpoint(app_t* app, endpoint_t* endp)
{
ASSERT(endp != NULL)
ASSERT(endpoints.HasItem(endp))
TRACE(("Endpoint %ld (%p) removed", endp->id, endp))
endpoints.RemoveItem(endp);
if (endp->consumer)
{
DisconnectDeadConsumer(endp);
}
BMessage notify;
notify.what = MSG_ENDPOINT_DELETED;
notify.AddInt32("midi:id", endp->id);
NotifyAll(&notify, app);
delete endp;
#ifdef DEBUG
DumpEndpoints();
#endif
}
//------------------------------------------------------------------------------
void MidiServerApp::DisconnectDeadConsumer(endpoint_t* cons)
{
ASSERT(cons != NULL)
ASSERT(cons->consumer)
for (int32 t = 0; t < CountEndpoints(); ++t)
{
endpoint_t* prod = EndpointAt(t);
if (!prod->consumer)
{
prod->connections.RemoveItem(cons);
}
}
}
//------------------------------------------------------------------------------
void MidiServerApp::MakeCreatedNotification(BMessage* msg, endpoint_t* endp)
{
ASSERT(msg != NULL)
ASSERT(endp != NULL)
msg->MakeEmpty();
msg->what = MSG_ENDPOINT_CREATED;
msg->AddInt32("midi:id", endp->id);
msg->AddBool("midi:consumer", endp->consumer);
msg->AddBool("midi:registered", endp->registered);
msg->AddString("midi:name", endp->name);
msg->AddMessage("midi:properties", &endp->properties);
if (endp->consumer)
{
msg->AddInt32("midi:port", endp->port);
msg->AddInt64("midi:latency", endp->latency);
}
}
//------------------------------------------------------------------------------
void MidiServerApp::MakeConnectedNotification(
BMessage* msg, endpoint_t* prod, endpoint_t* cons, bool mustConnect)
{
ASSERT(msg != NULL)
ASSERT(prod != NULL)
ASSERT(cons != NULL)
ASSERT(!prod->consumer)
ASSERT(cons->consumer)
msg->MakeEmpty();
if (mustConnect)
{
msg->what = MSG_ENDPOINTS_CONNECTED;
}
else
{
msg->what = MSG_ENDPOINTS_DISCONNECTED;
}
msg->AddInt32("midi:producer", prod->id);
msg->AddInt32("midi:consumer", cons->id);
}
//------------------------------------------------------------------------------
app_t* MidiServerApp::WhichApp(BMessage* msg)
{
ASSERT(msg != NULL)
BMessenger retadr = msg->ReturnAddress();
for (int32 t = 0; t < CountApps(); ++t)
{
app_t* app = AppAt(t);
if (app->messenger.Team() == retadr.Team())
{
return app;
}
}
TRACE(("Application %ld is not registered", retadr.Team()))
return NULL;
}
//------------------------------------------------------------------------------
endpoint_t* MidiServerApp::WhichEndpoint(BMessage* msg, app_t* app)
{
ASSERT(msg != NULL)
ASSERT(app != NULL)
int32 id;
if (msg->FindInt32("midi:id", &id) == B_OK)
{
endpoint_t* endp = FindEndpoint(id);
if ((endp != NULL) && (endp->app == app))
{
return endp;
}
}
TRACE(("Endpoint not found or wrong app"))
return NULL;
}
//------------------------------------------------------------------------------
endpoint_t* MidiServerApp::FindEndpoint(int32 id)
{
if (id > 0)
{
for (int32 t = 0; t < CountEndpoints(); ++t)
{
endpoint_t* endp = EndpointAt(t);
if (endp->id == id)
{
return endp;
}
}
}
TRACE(("Endpoint %ld not found", id))
return NULL;
}
//------------------------------------------------------------------------------
void MidiServerApp::NotifyAll(BMessage* msg, app_t* except)
{
ASSERT(msg != NULL)
for (int32 t = CountApps() - 1; t >= 0; --t)
{
app_t* app = AppAt(t);
if (app != except)
{
if (!SendNotification(app, msg))
{
delete (app_t*) apps.RemoveItem(t);
#ifdef DEBUG
DumpApps();
#endif
}
}
}
}
//------------------------------------------------------------------------------
bool MidiServerApp::SendNotification(app_t* app, BMessage* msg)
{
ASSERT(app != NULL)
ASSERT(msg != NULL)
status_t err = app->messenger.SendMessage(msg, (BHandler*) NULL, TIMEOUT);
if (err != B_OK)
{
DeliveryError(app);
}
return (err == B_OK);
}
//------------------------------------------------------------------------------
bool MidiServerApp::SendReply(app_t* app, BMessage* msg, BMessage* reply)
{
ASSERT(msg != NULL)
ASSERT(reply != NULL)
status_t err = msg->SendReply(reply, (BHandler*) NULL, TIMEOUT);
if ((err != B_OK) && (app != NULL))
{
DeliveryError(app);
apps.RemoveItem(app);
delete app;
#ifdef DEBUG
DumpApps();
#endif
}
return (err == B_OK);
}
//------------------------------------------------------------------------------
void MidiServerApp::DeliveryError(app_t* app)
{
ASSERT(app != NULL)
// We cannot communicate with the app, so we assume it's
// dead. We need to remove its endpoints from the roster,
// but we cannot do that right away; removing endpoints
// triggers a bunch of new notifications and we don't want
// those to get in the way of the notifications we are
// currently sending out. Instead, we consider the death
// of an app as a separate event, and pretend that the
// now-dead app sent us delete requests for its endpoints.
TRACE(("Delivery error; unregistering app (%p)", app))
BMessage msg;
for (int32 t = 0; t < CountEndpoints(); ++t)
{
endpoint_t* endp = EndpointAt(t);
if (endp->app == app)
{
msg.MakeEmpty();
msg.what = MSG_PURGE_ENDPOINT;
msg.AddInt32("midi:id", endp->id);
// It is not safe to post a message to your own
// looper's message queue, because you risk a
// deadlock if the queue is full. The chance of
// that happening is fairly small, but just in
// case, we catch it with a timeout. Because this
// situation is so unlikely, I decided to simply
// forget about the whole "purge" message then.
if (be_app_messenger.SendMessage(
&msg, (BHandler*) NULL, TIMEOUT) != B_OK)
{
WARN("Could not deliver purge message")
}
}
}
}
//------------------------------------------------------------------------------
int32 MidiServerApp::CountApps()
{
return apps.CountItems();
}
//------------------------------------------------------------------------------
app_t* MidiServerApp::AppAt(int32 index)
{
ASSERT(index >= 0 && index < CountApps())
return (app_t*) apps.ItemAt(index);
}
//------------------------------------------------------------------------------
int32 MidiServerApp::CountEndpoints()
{
return endpoints.CountItems();
}
//------------------------------------------------------------------------------
endpoint_t* MidiServerApp::EndpointAt(int32 index)
{
ASSERT(index >= 0 && index < CountEndpoints())
return (endpoint_t*) endpoints.ItemAt(index);
}
//------------------------------------------------------------------------------
int32 MidiServerApp::CountConnections(endpoint_t* prod)
{
ASSERT(prod != NULL)
ASSERT(!prod->consumer)
return prod->connections.CountItems();
}
//------------------------------------------------------------------------------
endpoint_t* MidiServerApp::ConnectionAt(endpoint_t* prod, int32 index)
{
ASSERT(prod != NULL)
ASSERT(!prod->consumer)
ASSERT(index >= 0 && index < CountConnections(prod))
return (endpoint_t*) prod->connections.ItemAt(index);
}
//------------------------------------------------------------------------------
#ifdef DEBUG
void MidiServerApp::DumpApps()
{
printf("*** START DumpApps\n");
for (int32 t = 0; t < CountApps(); ++t)
{
app_t* app = AppAt(t);
printf("\tapp %ld (%p): team %ld\n", t, app, app->messenger.Team());
}
printf("*** END DumpApps\n");
}
#endif
//------------------------------------------------------------------------------
#ifdef DEBUG
void MidiServerApp::DumpEndpoints()
{
printf("*** START DumpEndpoints\n");
for (int32 t = 0; t < CountEndpoints(); ++t)
{
endpoint_t* endp = EndpointAt(t);
printf("\tendpoint %ld (%p):\n", t, endp);
printf("\t\tid %ld, name '%s', %s, %s, app %p\n",
endp->id, endp->name.String(),
endp->consumer ? "consumer" : "producer",
endp->registered ? "registered" : "unregistered",
endp->app);
printf("\t\tproperties: "); endp->properties.PrintToStream();
if (endp->consumer)
{
printf("\t\tport %ld, latency %Ld\n", endp->port, endp->latency);
}
else
{
printf("\t\tconnections:\n");
for (int32 k = 0; k < CountConnections(endp); ++k)
{
endpoint_t* cons = ConnectionAt(endp, k);
printf("\t\t\tid %ld (%p)\n", cons->id, cons);
}
}
}
printf("*** END DumpEndpoints\n");
}
#endif
//------------------------------------------------------------------------------
@@ -88,4 +829,3 @@ int main()
}
//------------------------------------------------------------------------------
+126 -12
View File
@@ -2,41 +2,155 @@
* @file MidiServerApp.h
*
* @author Matthijs Hollemans
* @author Jerome Leveque
*/
#ifndef MIDI_SERVER_APP_H
#define MIDI_SERVER_APP_H
#include <Application.h>
#include <List.h>
#define MIDI_SERVER_SIGNATURE "application/x-vnd.OpenBeOS-midi-server"
struct app_t;
struct endpoint_t;
/**
* The BApplication that drives the midi_server.
* The heart of the midi_server. This BApplication subclass
* keeps the roster of endpoints and applications, processes
* incoming messages from libmidi2.so, and notifies the apps
* when something interesting happens.
*/
class MidiServerApp : public BApplication
{
public:
MidiServerApp();
virtual ~MidiServerApp();
virtual void AboutRequested();
virtual void MessageReceived(BMessage* msg);
//int32 GetNextFreeID();
//BMidiEndpoint* NextEndPoint(int32* id);
//BMidiRoster* GetRoster();
private:
/** Our superclass. */
typedef BApplication super;
//BList* endpoints;
//int32 nextFreeID;
//BMidiRoster* roster;
void OnRegisterApp(BMessage* msg);
void OnCreateEndpoint(BMessage* msg);
void OnDeleteEndpoint(BMessage* msg);
void OnPurgeEndpoint(BMessage* msg);
void OnChangeEndpoint(BMessage* msg);
void OnConnectDisconnect(BMessage* msg);
/**
* Sends an app MSG_ENDPOINT_CREATED notifications for
* all current endpoints. Used when the app registers.
*/
bool SendAllEndpoints(app_t* app);
/**
* Sends an app MSG_ENDPOINTS_CONNECTED notifications for
* all current connections. Used when the app registers.
*/
bool SendAllConnections(app_t* app);
/**
* Adds the specified endpoint to the roster, and notifies
* all other applications about this event.
*/
void AddEndpoint(BMessage* msg, endpoint_t* endp);
/**
* Removes an endpoint from the roster, and notifies all
* other apps about this event. "app" is the application
* that the endpoint belongs to; if it is NULL, the app
* no longer exists and we're purging the endpoint.
*/
void RemoveEndpoint(app_t* app, endpoint_t* endp);
/**
* Removes a consumer from the list of connections of
* all the producers it is connected to, just before
* we remove it from the roster.
*/
void DisconnectDeadConsumer(endpoint_t* cons);
/** Fills up a MSG_ENDPOINT_CREATED message. */
void MakeCreatedNotification(BMessage* msg, endpoint_t* endp);
/** Fills up a MSG_ENDPOINTS_(DIS)CONNECTED message. */
void MakeConnectedNotification(
BMessage* msg, endpoint_t* prod, endpoint_t* cons, bool mustConnect);
/**
* Figures out which application a message came from.
* Returns NULL if the application is not registered.
*/
app_t* WhichApp(BMessage* msg);
/**
* Looks at the "midi:id" field from a message, and returns
* the endpoint object that corresponds to that ID. It also
* checks whether the application specified by "app" really
* owns the endpoint. Returns NULL on error.
*/
endpoint_t* WhichEndpoint(BMessage* msg, app_t* app);
/**
* Returns the endpoint with the specified ID, or
* NULL if no such endpoint exists on the roster.
*/
endpoint_t* FindEndpoint(int32 id);
/**
* Sends notification messages to all registered apps,
* except to the application that triggered the event.
* The "except" app is allowed to be NULL.
*/
void NotifyAll(BMessage* msg, app_t* except);
/**
* Sends a notification message to an application, which is
* not necessarily registered yet. Applications never reply
* to such notification messages.
*/
bool SendNotification(app_t* app, BMessage* msg);
/**
* Sends a reply to a request made by an application.
* If "app" is NULL, the application is not registered
* (and the reply should contain an error code).
*/
bool SendReply(app_t* app, BMessage* msg, BMessage* reply);
/**
* Removes an app and all of its endpoints from the roster
* if a reply or notification message cannot be delivered.
* (Waiting for communications to fail is actually our only
* way to get rid of stale endpoints.)
*/
void DeliveryError(app_t* app);
int32 CountApps();
app_t* AppAt(int32 index);
int32 CountEndpoints();
endpoint_t* EndpointAt(int32 index);
int32 CountConnections(endpoint_t* prod);
endpoint_t* ConnectionAt(endpoint_t* prod, int32 index);
/** The registered applications. */
BList apps;
/** All the endpoints in the system. */
BList endpoints;
/** The ID we will assign to the next new endpoint. */
int32 nextId;
#ifdef DEBUG
void DumpApps();
void DumpEndpoints();
#endif
};
#endif // MIDI_SERVER_APP_H
+74
View File
@@ -0,0 +1,74 @@
/**
* @file ServerDefs.h
*
* Additional data types used by MidiServerApp.
*
* @author Matthijs Hollemans
*/
#ifndef MIDI_SERVER_DEFS_H
#define MIDI_SERVER_DEFS_H
#include <List.h>
#include <Message.h>
#include <Messenger.h>
#include <String.h>
/** Describes an application that registered with the midi_server. */
struct app_t
{
/** For sending notifications to the app. */
BMessenger messenger;
};
/**
* Describes a MIDI endpoint. The endpoint_t structure is
* used to describe both consumer and producer endpoints.
*/
struct endpoint_t
{
#ifdef DEBUG
endpoint_t()
{
app = (app_t*) 0xbaadc0de;
}
#endif
/** The application that owns this endpoint. */
app_t* app;
/**
* The endpoint's system-wide ID, which is assigned
* by the midi_server when the endpoint is created.
*/
int32 id;
/** Is this a consumer or producer endpoint? */
bool consumer;
/** Whether this endpoint is visible to all applications. */
bool registered;
/** The endpoint's human-readable name. */
BString name;
/** User-defined attributes. */
BMessage properties;
/** The port that accepts MIDI events (consumer only). */
port_id port;
/**
* How long it takes this endpoint to process incoming
* MIDI events (consumer only).
*/
bigtime_t latency;
/**
* Which consumers this endpoint sprays outgoing MIDI
* events to (producer only).
*/
BList connections;
};
#endif // MIDI_SERVER_DEFS_H