1 /* 2 * OpenSound media addon for BeOS and Haiku 3 * 4 * Copyright (c) 2007, François Revol (revol@free.fr) 5 * Distributed under the terms of the MIT License. 6 * 7 * Based on MultiAudio media addon 8 * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) 9 */ 10 #include "OpenSoundAddOn.h" 11 12 #include <MediaDefs.h> 13 #include <MediaAddOn.h> 14 #include <Errors.h> 15 #include <Node.h> 16 #include <Mime.h> 17 #include <StorageDefs.h> 18 #include <Path.h> 19 #include <Directory.h> 20 #include <Entry.h> 21 #include <FindDirectory.h> 22 #include <Debug.h> 23 #include <errno.h> 24 25 #include "OpenSoundNode.h" 26 #include "OpenSoundDevice.h" 27 #include "OpenSoundDeviceEngine.h" 28 29 #include <limits.h> 30 #include <stdio.h> 31 #include <string.h> 32 33 #include "debug.h" 34 35 #define MULTI_SAVE 36 37 38 // instantiation function 39 extern "C" _EXPORT BMediaAddOn * make_media_addon(image_id image) { 40 CALLED(); 41 return new OpenSoundAddOn(image); 42 } 43 44 // -------------------------------------------------------- // 45 // ctor/dtor 46 // -------------------------------------------------------- // 47 48 OpenSoundAddOn::~OpenSoundAddOn() 49 { 50 CALLED(); 51 52 void *device = NULL; 53 for (int32 i = 0; (device = fDevices.ItemAt(i)); i++) 54 delete (OpenSoundDevice *)device; 55 56 SaveSettings(); 57 } 58 59 OpenSoundAddOn::OpenSoundAddOn(image_id image) : 60 BMediaAddOn(image), 61 fDevices() 62 { 63 CALLED(); 64 fInitCheckStatus = B_NO_INIT; 65 66 /* unix paths */ 67 if (RecursiveScan("/dev/oss/") != B_OK) 68 return; 69 /* 70 if (RecursiveScan("/dev/audio/oss/") != B_OK) 71 return; 72 */ 73 74 LoadSettings(); 75 76 fInitCheckStatus = B_OK; 77 } 78 79 // -------------------------------------------------------- // 80 // BMediaAddOn impl 81 // -------------------------------------------------------- // 82 83 status_t OpenSoundAddOn::InitCheck( 84 const char ** out_failure_text) 85 { 86 CALLED(); 87 return B_OK; 88 } 89 90 int32 OpenSoundAddOn::CountFlavors() 91 { 92 CALLED(); 93 PRINT(("%" B_PRId32 " flavours\n", fDevices.CountItems())); 94 return fDevices.CountItems(); 95 } 96 97 status_t OpenSoundAddOn::GetFlavorAt( 98 int32 n, 99 const flavor_info ** out_info) 100 { 101 CALLED(); 102 if (n < 0 || n > fDevices.CountItems() - 1) { 103 fprintf(stderr, "<- B_BAD_INDEX\n"); 104 return B_BAD_INDEX; 105 } 106 107 OpenSoundDevice *device = (OpenSoundDevice *) fDevices.ItemAt(n); 108 109 flavor_info * infos = new flavor_info[1]; 110 OpenSoundNode::GetFlavor(&infos[0], n); 111 infos[0].name = device->fCardInfo.longname; 112 (*out_info) = infos; 113 return B_OK; 114 } 115 116 BMediaNode * OpenSoundAddOn::InstantiateNodeFor( 117 const flavor_info * info, 118 BMessage * config, 119 status_t * out_error) 120 { 121 CALLED(); 122 123 OpenSoundDevice *device = (OpenSoundDevice*)fDevices.ItemAt( 124 info->internal_id); 125 if (device == NULL) { 126 *out_error = B_ERROR; 127 return NULL; 128 } 129 130 #ifdef MULTI_SAVE 131 if (fSettings.FindMessage(device->fCardInfo.longname, config) == B_OK) { 132 fSettings.RemoveData(device->fCardInfo.longname); 133 } 134 #endif 135 136 OpenSoundNode * node = 137 new OpenSoundNode(this, 138 device->fCardInfo.longname, 139 device, 140 info->internal_id, 141 config); 142 if (node == 0) { 143 *out_error = B_NO_MEMORY; 144 fprintf(stderr, "<- B_NO_MEMORY\n"); 145 } else { 146 *out_error = node->InitCheck(); 147 } 148 return node; 149 } 150 151 status_t 152 OpenSoundAddOn::GetConfigurationFor(BMediaNode * your_node, BMessage * into_message) 153 { 154 CALLED(); 155 #ifdef MULTI_SAVE 156 { 157 into_message = new BMessage(); 158 OpenSoundNode * node = dynamic_cast<OpenSoundNode*>(your_node); 159 if (node == 0) { 160 fprintf(stderr, "<- B_BAD_TYPE\n"); 161 return B_BAD_TYPE; 162 } 163 if (node->GetConfigurationFor(into_message) == B_OK) { 164 fSettings.AddMessage(your_node->Name(), into_message); 165 } 166 return B_OK; 167 } 168 #endif 169 // currently never called by the media kit. Seems it is not implemented. 170 OpenSoundNode * node = dynamic_cast<OpenSoundNode*>(your_node); 171 if (node == 0) { 172 fprintf(stderr, "<- B_BAD_TYPE\n"); 173 return B_BAD_TYPE; 174 } 175 return node->GetConfigurationFor(into_message); 176 } 177 178 179 bool OpenSoundAddOn::WantsAutoStart() 180 { 181 CALLED(); 182 return false; 183 } 184 185 status_t OpenSoundAddOn::AutoStart( 186 int in_count, 187 BMediaNode ** out_node, 188 int32 * out_internal_id, 189 bool * out_has_more) 190 { 191 CALLED(); 192 return B_OK; 193 } 194 195 status_t 196 OpenSoundAddOn::RecursiveScan(const char* rootPath, BEntry *rootEntry) 197 { 198 status_t err; 199 int mixer; 200 oss_sysinfo sysinfo; 201 oss_card_info cardinfo; 202 int card, i, j; 203 BList devs; 204 205 CALLED(); 206 207 // make sure directories are scanned in this order 208 BDirectory("/dev/audio/hmulti"); 209 BDirectory("/dev/audio/old"); 210 // OSS last, to give precedence to native drivers. 211 // If other addons are loaded first it's ok as well. 212 // Also, we must open it to make sure oss_loader is here, 213 // else we don't get /dev/sndstat since we don't have a symlink in dev/. 214 BDirectory("/dev/oss"); 215 216 mixer = open(OSS_MIXER_DEV, O_RDWR); 217 if (mixer < 0) { 218 // try to rescan 219 // only works in BeOS 220 BFile fDevFS("/dev/.", B_WRITE_ONLY); 221 const char *drv = "oss_loader"; 222 fDevFS.Write(drv, strlen(drv)); 223 mixer = open(OSS_MIXER_DEV, O_RDWR); 224 if (mixer < 0) { 225 err = errno; 226 goto err0; 227 } 228 } 229 230 if (ioctl(mixer, SNDCTL_SYSINFO, &sysinfo) < 0) { 231 err = errno; 232 goto err1; 233 } 234 235 PRINT(("OSS: %s %s (0x%08X)\n", sysinfo.product, sysinfo.version, sysinfo.versionnum)); 236 PRINT(("OSS: %d audio cards, %d audio devs, %d audio engines, %d midi, %d mixers\n", sysinfo.numcards, sysinfo.numaudios, sysinfo.numaudioengines, sysinfo.nummidis, sysinfo.nummixers)); 237 238 /* construct an empty SoundDevice per card */ 239 240 for (card = 0; card < sysinfo.numcards; card++) { 241 cardinfo.card = card; 242 if (ioctl(mixer, SNDCTL_CARDINFO, &cardinfo) < 0) { 243 err = errno; 244 goto err1; 245 } 246 OpenSoundDevice *device = new OpenSoundDevice(&cardinfo); 247 if (device) 248 devs.AddItem(device); 249 else { 250 err = ENOMEM; 251 goto err1; 252 } 253 } 254 255 /* Add its audio engines to it */ 256 257 for (i = 0; i < sysinfo.numaudioengines; i++) { 258 oss_audioinfo audioinfo; 259 audioinfo.dev = i; 260 if (ioctl(mixer, SNDCTL_ENGINEINFO, &audioinfo, sizeof(oss_audioinfo)) < 0) { 261 err = errno; 262 goto err1; 263 } 264 PRINT(("OSS: engine[%d]: card=%d, port=%d, legacy=%d, next_play=%d, next_rec=%d\n", i, audioinfo.card_number, audioinfo.port_number, audioinfo.legacy_device, audioinfo.next_play_engine, audioinfo.next_rec_engine)); 265 OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(audioinfo.card_number)); 266 if (device) 267 device->AddEngine(&audioinfo); 268 } 269 270 /* Add its mixers to it */ 271 272 for (i = 0; i < sysinfo.nummixers; i++) { 273 oss_mixerinfo mixerinfo; 274 mixerinfo.dev = i; 275 if (ioctl(mixer, SNDCTL_MIXERINFO, &mixerinfo) < 0) { 276 err = errno; 277 goto err1; 278 } 279 PRINT(("OSS: mixer[%d]: card=%d\n", i, mixerinfo.card_number)); 280 OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(mixerinfo.card_number)); 281 if (device) 282 device->AddMixer(&mixerinfo); 283 } 284 285 /* resolve engine chains of shadow engines */ 286 287 for (card = 0; card < sysinfo.numcards; card++) { 288 OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(card)); 289 if (!device) 290 continue; 291 for (i = 0; i < device->CountEngines(); i++) { 292 OpenSoundDeviceEngine *engine = device->EngineAt(i); 293 if (engine) { 294 if (engine->Info()->next_play_engine) { 295 for (j = 0; j < device->CountEngines(); j++) { 296 OpenSoundDeviceEngine *next = device->EngineAt(j); 297 if (!next || (engine == next)) 298 continue; 299 if (next->Info()->dev == engine->Info()->next_play_engine) { 300 PRINT(("OSS: engine[%d].next_play = engine[%d]\n", i, j)); 301 engine->fNextPlay = next; 302 break; 303 } 304 } 305 } 306 if (engine->Info()->next_rec_engine) { 307 for (j = 0; j < device->CountEngines(); j++) { 308 OpenSoundDeviceEngine *next = device->EngineAt(j); 309 if (!next || (engine == next)) 310 continue; 311 if (next->Info()->dev == engine->Info()->next_rec_engine) { 312 PRINT(("OSS: engine[%d].next_rec = engine[%d]\n", i, j)); 313 engine->fNextRec = next; 314 break; 315 } 316 } 317 } 318 } 319 } 320 } 321 322 /* copy correctly initialized devs to fDevices */ 323 324 for (card = 0; card < sysinfo.numcards; card++) { 325 OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(card)); 326 if (device) { 327 if (card == 0) { /* skip the "oss0" pseudo card device */ 328 delete device; 329 continue; 330 } 331 if ((device->InitDriver() == B_OK) && (device->InitCheck() == B_OK)) 332 fDevices.AddItem(device); 333 else 334 delete device; 335 } 336 } 337 if (fDevices.CountItems()) 338 err = B_OK; 339 else 340 err = ENOENT; 341 err1: 342 close(mixer); 343 err0: 344 return err; 345 346 #if MA 347 BDirectory root; 348 if (rootEntry != NULL) 349 root.SetTo(rootEntry); 350 else if (rootPath != NULL) { 351 root.SetTo(rootPath); 352 } else { 353 PRINT(("Error in OpenSoundAddOn::RecursiveScan null params\n")); 354 return B_ERROR; 355 } 356 357 BEntry entry; 358 359 while (root.GetNextEntry(&entry) > B_ERROR) { 360 361 if (entry.IsDirectory()) { 362 BPath path; 363 entry.GetPath(&path); 364 OpenSoundDevice *device = new OpenSoundDevice(path.Path() + strlen(rootPath), path.Path()); 365 if (device) { 366 if (device->InitCheck() == B_OK) 367 fDevices.AddItem(device); 368 else 369 delete device; 370 } 371 } 372 } 373 374 return B_OK; 375 #endif 376 } 377 378 379 void 380 OpenSoundAddOn::SaveSettings(void) 381 { 382 CALLED(); 383 BPath path; 384 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 385 path.Append(SETTINGS_FILE); 386 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 387 if (file.InitCheck() == B_OK) 388 fSettings.Flatten(&file); 389 } 390 } 391 392 393 void 394 OpenSoundAddOn::LoadSettings(void) 395 { 396 CALLED(); 397 fSettings.MakeEmpty(); 398 399 BPath path; 400 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 401 path.Append(SETTINGS_FILE); 402 BFile file(path.Path(), B_READ_ONLY); 403 if ((file.InitCheck() == B_OK) && (fSettings.Unflatten(&file) == B_OK)) 404 { 405 PRINT_OBJECT(fSettings); 406 } else { 407 PRINT(("Error unflattening settings file %s\n", path.Path())); 408 } 409 } 410 } 411 412 413 void 414 OpenSoundAddOn::RegisterMediaFormats(void) 415 { 416 CALLED(); 417 // register non-raw audio formats to the Media Kit 418 #ifdef ENABLE_NON_RAW_SUPPORT 419 OpenSoundDevice::register_media_formats(); 420 #endif 421 422 423 } 424