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