1/* 2 * Copyright 2006, Haiku. 3 * 4 * Copyright (c) 2002-2004 Matthijs Hollemans 5 * Distributed under the terms of the MIT License. 6 * 7 * Authors: 8 * Matthijs Hollemans 9 */ 10 11#include "debug.h" 12#include <MidiConsumer.h> 13#include <MidiRoster.h> 14#include "MidiRosterLooper.h" 15#include "protocol.h" 16 17using namespace BPrivate; 18 19// The midi_debug_level and midi_dispatcher_priority symbols 20// were exported by Be's libmidi2, and even though they do not 21// appear in the headers, some apps may still be using them. 22// For backwards compatibility's sake, we export those symbols 23// as well, even though we do not use them for anything. 24 25// Not used. For backwards compatibility only. 26int32 midi_debug_level = 0; 27 28// Not used. For backwards compatibility only. 29int32 midi_dispatcher_priority = B_REAL_TIME_PRIORITY; 30 31//------------------------------------------------------------------------------ 32 33// The one and only BMidiRoster instance, which is created 34// the first time the client app calls MidiRoster(). It is 35// destroyed by the BMidiRosterKiller when the app quits. 36static BMidiRoster* roster = NULL; 37 38// Destroys the BMidiRoster instance when the app quits. 39namespace BPrivate 40{ 41 static struct BMidiRosterKiller 42 { 43 ~BMidiRosterKiller() 44 { 45 delete roster; 46 } 47 } 48 midi_roster_killer; 49} 50 51 52BMidiEndpoint* 53BMidiRoster::NextEndpoint(int32* id) 54{ 55 BMidiEndpoint* endp = NULL; 56 57 if (id != NULL) { 58 BMidiRosterLooper* looper = MidiRoster()->fLooper; 59 if (looper->Lock()) { 60 endp = looper->NextEndpoint(id); 61 if (endp != NULL) { 62 endp->Acquire(); 63 } 64 looper->Unlock(); 65 } 66 } 67 68 return endp; 69} 70 71 72BMidiProducer* 73BMidiRoster::NextProducer(int32* id) 74{ 75 BMidiEndpoint* endp; 76 77 while ((endp = NextEndpoint(id)) != NULL) { 78 if (endp->IsProducer()) { 79 return (BMidiProducer*) endp; 80 } 81 endp->Release(); 82 } 83 84 return NULL; 85} 86 87 88BMidiConsumer* 89BMidiRoster::NextConsumer(int32* id) 90{ 91 BMidiEndpoint* endp; 92 93 while ((endp = NextEndpoint(id)) != NULL) { 94 if (endp->IsConsumer()) { 95 return (BMidiConsumer*) endp; 96 } 97 endp->Release(); 98 } 99 100 return NULL; 101} 102 103 104BMidiEndpoint* 105BMidiRoster::FindEndpoint(int32 id, bool localOnly) 106{ 107 BMidiEndpoint* endp = NULL; 108 109 BMidiRosterLooper* looper = MidiRoster()->fLooper; 110 if (looper->Lock()) { 111 endp = looper->FindEndpoint(id); 112 113 if ((endp != NULL) && endp->IsRemote()) { 114 if (localOnly || !endp->IsRegistered()) { 115 endp = NULL; 116 } 117 } 118 119 if (endp != NULL) { 120 endp->Acquire(); 121 } 122 123 looper->Unlock(); 124 } 125 126 return endp; 127} 128 129 130BMidiProducer* 131BMidiRoster::FindProducer(int32 id, bool localOnly) 132{ 133 BMidiEndpoint* endp = FindEndpoint(id, localOnly); 134 135 if ((endp != NULL) && !endp->IsProducer()) { 136 endp->Release(); 137 endp = NULL; 138 } 139 140 return (BMidiProducer*) endp; 141} 142 143 144BMidiConsumer* 145BMidiRoster::FindConsumer(int32 id, bool localOnly) 146{ 147 BMidiEndpoint* endp = FindEndpoint(id, localOnly); 148 149 if ((endp != NULL) && !endp->IsConsumer()) { 150 endp->Release(); 151 endp = NULL; 152 } 153 154 return (BMidiConsumer*) endp; 155} 156 157 158void 159BMidiRoster::StartWatching(const BMessenger* msngr) 160{ 161 if (msngr == NULL) { 162 WARN("StartWatching does not accept a NULL messenger") 163 } else { 164 BMidiRosterLooper* looper = MidiRoster()->fLooper; 165 if (looper->Lock()) { 166 looper->StartWatching(msngr); 167 looper->Unlock(); 168 } 169 } 170} 171 172 173void 174BMidiRoster::StopWatching() 175{ 176 BMidiRosterLooper* looper = MidiRoster()->fLooper; 177 if (looper->Lock()) { 178 looper->StopWatching(); 179 looper->Unlock(); 180 } 181} 182 183 184status_t 185BMidiRoster::Register(BMidiEndpoint* endp) 186{ 187 if (endp != NULL) { 188 return endp->Register(); 189 } 190 191 return B_BAD_VALUE; 192} 193 194 195status_t 196BMidiRoster::Unregister(BMidiEndpoint* endp) 197{ 198 if (endp != NULL) { 199 return endp->Unregister(); 200 } 201 202 return B_BAD_VALUE; 203} 204 205 206BMidiRoster* 207BMidiRoster::MidiRoster() 208{ 209 if (roster == NULL) { 210 new BMidiRoster(); 211 } 212 213 return roster; 214} 215 216 217BMidiRoster::BMidiRoster() 218{ 219 TRACE(("BMidiRoster::BMidiRoster")) 220 221 // While our constructor is executing, some function may 222 // call MidiRoster() again, which causes an endless loop. 223 // To prevent this, we immediately fill in "roster"; now 224 // subsequent calls to MidiRoster() won't mess up things. 225 226 roster = this; 227 228 fLooper = new BMidiRosterLooper(); 229 230 if (!fLooper->Init(this)) 231 return; 232 233 BMessage msg; 234 msg.what = MSG_REGISTER_APP; 235 msg.AddMessenger("midi:messenger", BMessenger(fLooper)); 236 fServer = new BMessenger(MIDI_SERVER_SIGNATURE); 237 238 if (fServer->SendMessage(&msg, fLooper, TIMEOUT) != B_OK) { 239 WARN("Cannot send request to midi_server"); 240 return; 241 } 242 243 // Although unlikely, we may receive the midi_server's 244 // "app registered" reply before we lock the semaphore. 245 // In that case, BMidiRosterLooper's MessageReceived() 246 // will bump the semaphore count, and our acquire_sem() 247 // can grab the semaphore safely (without blocking). 248 249 acquire_sem(fLooper->fInitLock); 250} 251 252 253BMidiRoster::~BMidiRoster() 254{ 255 TRACE(("BMidiRoster::~BMidiRoster")) 256 257 if (fLooper != NULL) { 258 fLooper->Lock(); 259 fLooper->Quit(); 260 } 261 delete fServer; 262} 263 264 265void BMidiRoster::_Reserved1() { } 266void BMidiRoster::_Reserved2() { } 267void BMidiRoster::_Reserved3() { } 268void BMidiRoster::_Reserved4() { } 269void BMidiRoster::_Reserved5() { } 270void BMidiRoster::_Reserved6() { } 271void BMidiRoster::_Reserved7() { } 272void BMidiRoster::_Reserved8() { } 273 274 275void 276BMidiRoster::CreateLocal(BMidiEndpoint* endp) 277{ 278 ASSERT(endp != NULL) 279 280 // We are being called from the BMidiLocalConsumer or 281 // BMidiLocalProducer constructor, so there is no need 282 // to lock anything, because at this point there cannot 283 // be multiple threads accessing the endpoint's data. 284 285 BMessage msg; 286 msg.what = MSG_CREATE_ENDPOINT; 287 msg.AddBool("midi:consumer", endp->fIsConsumer); 288 msg.AddBool("midi:registered", endp->fIsRegistered); 289 msg.AddString("midi:name", endp->Name()); 290 msg.AddMessage("midi:properties", endp->fProperties); 291 292 if (endp->IsConsumer()) { 293 BMidiConsumer* consumer = (BMidiConsumer*) endp; 294 msg.AddInt32("midi:port", consumer->fPort); 295 msg.AddInt64("midi:latency", consumer->fLatency); 296 } 297 298 BMessage reply; 299 if (SendRequest(&msg, &reply) == B_OK) { 300 status_t res; 301 if (reply.FindInt32("midi:result", &res) == B_OK) { 302 if (res == B_OK) { 303 int32 id; 304 if (reply.FindInt32("midi:id", &id) == B_OK) { 305 endp->fId = id; 306 307 if (fLooper->Lock()) { 308 fLooper->AddEndpoint(endp); 309 fLooper->Unlock(); 310 } 311 } 312 } 313 } 314 } 315 316 // There are many things that can go wrong when creating 317 // a new endpoint, but BMidiEndpoint has no InitCheck() 318 // method to check for this. (You can, however, see if the 319 // endpoint's ID is 0 after the constructor returns, or 320 // call IsValid().) In any case, you should still Release() 321 // the endpoint to delete the object. (This is different 322 // from Be's implementation, which bumps the refcount only 323 // when creation succeeded. If you call Release(), you'll 324 // trip an assertion, so you can't delete these endpoints.) 325} 326 327 328void 329BMidiRoster::DeleteLocal(BMidiEndpoint* endp) 330{ 331 ASSERT(endp != NULL) 332 333 BMessage msg; 334 msg.what = MSG_DELETE_ENDPOINT; 335 msg.AddInt32("midi:id", endp->ID()); 336 337 // Note: this is always called from BMidiLocalConsumer's 338 // or BMidiLocalProducer's destructor, so we don't expect 339 // a reply from the server. If something went wrong, the 340 // object will be destroyed regardless. 341 342 fServer->SendMessage(&msg, (BHandler*) NULL, TIMEOUT); 343 344 // If the endpoint was successfully created, we must remove 345 // it from the list of endpoints. If creation failed, then 346 // we didn't put the endpoint on that list. If the endpoint 347 // was connected to anything, we must also disconnect it. 348 349 if (endp->ID() > 0) { 350 if (fLooper->Lock()) { 351 fLooper->RemoveEndpoint(endp); 352 fLooper->Unlock(); 353 } 354 } 355} 356 357 358status_t 359BMidiRoster::SendRequest(BMessage* msg, BMessage* reply) 360{ 361 ASSERT(msg != NULL) 362 ASSERT(reply != NULL) 363 364 status_t err = fServer->SendMessage(msg, reply, TIMEOUT, TIMEOUT); 365 366 if (err != B_OK) { 367 WARN("Cannot send request to midi_server"); 368 } 369 370 #ifdef DEBUG 371 if (err == B_OK) { 372 printf("REPLY "); reply->PrintToStream(); 373 } 374 #endif 375 376 return err; 377} 378 379