Files
haiku-beta6/src/kits/midi2/MidiRoster.cpp
T
mahlzeit 263f2c6006 Added MIT license.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2883 a95241bf-73f2-0310-859d-f6bbb57e9c96
2003-03-11 20:38:00 +00:00

430 lines
10 KiB
C++

/*
* Copyright (c) 2002-2003 Matthijs Hollemans
*
* 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.
*/
#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)
{
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)
{
BMidiEndpoint* endp;
while ((endp = NextEndpoint(id)) != NULL)
{
if (endp->IsProducer())
{
return (BMidiProducer*) endp;
}
else
{
endp->Release();
}
}
return NULL;
}
//------------------------------------------------------------------------------
BMidiConsumer* BMidiRoster::NextConsumer(int32* id)
{
BMidiEndpoint* endp;
while ((endp = NextEndpoint(id)) != NULL)
{
if (endp->IsConsumer())
{
return (BMidiConsumer*) endp;
}
else
{
endp->Release();
}
}
return NULL;
}
//------------------------------------------------------------------------------
BMidiEndpoint* BMidiRoster::FindEndpoint(int32 id, bool localOnly)
{
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;
}
//------------------------------------------------------------------------------
BMidiConsumer* BMidiRoster::FindConsumer(int32 id, bool localOnly)
{
BMidiEndpoint* endp = FindEndpoint(id, localOnly);
if ((endp != NULL) && !endp->IsConsumer())
{
endp->Release();
endp = NULL;
}
return (BMidiConsumer*) endp;
}
//------------------------------------------------------------------------------
void BMidiRoster::StartWatching(const BMessenger* msngr)
{
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()
{
BMidiRosterLooper* looper = MidiRoster()->looper;
if (looper->Lock())
{
looper->StopWatching();
looper->Unlock();
}
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Register(BMidiEndpoint* endp)
{
if (endp != NULL)
{
return endp->Register();
}
return B_BAD_VALUE;
}
//------------------------------------------------------------------------------
status_t BMidiRoster::Unregister(BMidiEndpoint* endp)
{
if (endp != NULL)
{
return endp->Unregister();
}
return B_BAD_VALUE;
}
//------------------------------------------------------------------------------
BMidiRoster* BMidiRoster::MidiRoster()
{
if (roster == NULL)
{
new BMidiRoster();
}
return roster;
}
//------------------------------------------------------------------------------
BMidiRoster::BMidiRoster()
{
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()
{
TRACE(("BMidiRoster::~BMidiRoster"))
if (looper != NULL)
{
looper->Lock();
looper->Quit();
}
delete server;
}
//------------------------------------------------------------------------------
void BMidiRoster::_Reserved1() { }
void BMidiRoster::_Reserved2() { }
void BMidiRoster::_Reserved3() { }
void BMidiRoster::_Reserved4() { }
void BMidiRoster::_Reserved5() { }
void BMidiRoster::_Reserved6() { }
void BMidiRoster::_Reserved7() { }
void BMidiRoster::_Reserved8() { }
//------------------------------------------------------------------------------
void BMidiRoster::CreateLocal(BMidiEndpoint* endp)
{
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.)
}
//------------------------------------------------------------------------------
void BMidiRoster::DeleteLocal(BMidiEndpoint* endp)
{
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();
}
}
}
//------------------------------------------------------------------------------
status_t BMidiRoster::SendRequest(BMessage* msg, BMessage* reply)
{
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;
}
//------------------------------------------------------------------------------