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