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 17 using 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. 26 int32 midi_debug_level = 0; 27 28 // Not used. For backwards compatibility only. 29 int32 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. 36 static BMidiRoster* roster = NULL; 37 38 // Destroys the BMidiRoster instance when the app quits. 39 namespace BPrivate 40 { 41 static struct BMidiRosterKiller 42 { 43 ~BMidiRosterKiller() 44 { 45 delete roster; 46 } 47 } 48 midi_roster_killer; 49 } 50 51 52 BMidiEndpoint* 53 BMidiRoster::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 72 BMidiProducer* 73 BMidiRoster::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 88 BMidiConsumer* 89 BMidiRoster::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 104 BMidiEndpoint* 105 BMidiRoster::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 130 BMidiProducer* 131 BMidiRoster::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 144 BMidiConsumer* 145 BMidiRoster::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 158 void 159 BMidiRoster::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 173 void 174 BMidiRoster::StopWatching() 175 { 176 BMidiRosterLooper* looper = MidiRoster()->fLooper; 177 if (looper->Lock()) { 178 looper->StopWatching(); 179 looper->Unlock(); 180 } 181 } 182 183 184 status_t 185 BMidiRoster::Register(BMidiEndpoint* endp) 186 { 187 if (endp != NULL) { 188 return endp->Register(); 189 } 190 191 return B_BAD_VALUE; 192 } 193 194 195 status_t 196 BMidiRoster::Unregister(BMidiEndpoint* endp) 197 { 198 if (endp != NULL) { 199 return endp->Unregister(); 200 } 201 202 return B_BAD_VALUE; 203 } 204 205 206 BMidiRoster* 207 BMidiRoster::MidiRoster() 208 { 209 if (roster == NULL) { 210 new BMidiRoster(); 211 } 212 213 return roster; 214 } 215 216 217 BMidiRoster::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 253 BMidiRoster::~BMidiRoster() 254 { 255 TRACE(("BMidiRoster::~BMidiRoster")) 256 257 if (fLooper != NULL) { 258 fLooper->Lock(); 259 fLooper->Quit(); 260 } 261 delete fServer; 262 } 263 264 265 void BMidiRoster::_Reserved1() { } 266 void BMidiRoster::_Reserved2() { } 267 void BMidiRoster::_Reserved3() { } 268 void BMidiRoster::_Reserved4() { } 269 void BMidiRoster::_Reserved5() { } 270 void BMidiRoster::_Reserved6() { } 271 void BMidiRoster::_Reserved7() { } 272 void BMidiRoster::_Reserved8() { } 273 274 275 void 276 BMidiRoster::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 328 void 329 BMidiRoster::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 358 status_t 359 BMidiRoster::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