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