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