/* * OpenSound media addon for BeOS and Haiku * * Copyright (c) 2007, François Revol (revol@free.fr) * Distributed under the terms of the MIT License. * * Based on MultiAudio media addon * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) */ #include "OpenSoundAddOn.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "OpenSoundNode.h" #include "OpenSoundDevice.h" #include "OpenSoundDeviceEngine.h" #include #include #include #include "debug.h" #define MULTI_SAVE // instantiation function extern "C" _EXPORT BMediaAddOn * make_media_addon(image_id image) { CALLED(); return new OpenSoundAddOn(image); } // -------------------------------------------------------- // // ctor/dtor // -------------------------------------------------------- // OpenSoundAddOn::~OpenSoundAddOn() { CALLED(); void *device = NULL; for (int32 i = 0; (device = fDevices.ItemAt(i)); i++) delete (OpenSoundDevice *)device; SaveSettings(); } OpenSoundAddOn::OpenSoundAddOn(image_id image) : BMediaAddOn(image), fDevices() { CALLED(); fInitCheckStatus = B_NO_INIT; /* unix paths */ if (RecursiveScan("/dev/oss/") != B_OK) return; /* if (RecursiveScan("/dev/audio/oss/") != B_OK) return; */ LoadSettings(); fInitCheckStatus = B_OK; } // -------------------------------------------------------- // // BMediaAddOn impl // -------------------------------------------------------- // status_t OpenSoundAddOn::InitCheck( const char ** out_failure_text) { CALLED(); return B_OK; } int32 OpenSoundAddOn::CountFlavors() { CALLED(); PRINT(("%" B_PRId32 " flavours\n", fDevices.CountItems())); return fDevices.CountItems(); } status_t OpenSoundAddOn::GetFlavorAt( int32 n, const flavor_info ** out_info) { CALLED(); if (n < 0 || n > fDevices.CountItems() - 1) { fprintf(stderr, "<- B_BAD_INDEX\n"); return B_BAD_INDEX; } OpenSoundDevice *device = (OpenSoundDevice *) fDevices.ItemAt(n); flavor_info * infos = new flavor_info[1]; OpenSoundNode::GetFlavor(&infos[0], n); infos[0].name = device->fCardInfo.longname; (*out_info) = infos; return B_OK; } BMediaNode * OpenSoundAddOn::InstantiateNodeFor( const flavor_info * info, BMessage * config, status_t * out_error) { CALLED(); OpenSoundDevice *device = (OpenSoundDevice*)fDevices.ItemAt( info->internal_id); if (device == NULL) { *out_error = B_ERROR; return NULL; } #ifdef MULTI_SAVE if (fSettings.FindMessage(device->fCardInfo.longname, config) == B_OK) { fSettings.RemoveData(device->fCardInfo.longname); } #endif OpenSoundNode * node = new OpenSoundNode(this, device->fCardInfo.longname, device, info->internal_id, config); if (node == 0) { *out_error = B_NO_MEMORY; fprintf(stderr, "<- B_NO_MEMORY\n"); } else { *out_error = node->InitCheck(); } return node; } status_t OpenSoundAddOn::GetConfigurationFor(BMediaNode * your_node, BMessage * into_message) { CALLED(); #ifdef MULTI_SAVE { into_message = new BMessage(); OpenSoundNode * node = dynamic_cast(your_node); if (node == 0) { fprintf(stderr, "<- B_BAD_TYPE\n"); return B_BAD_TYPE; } if (node->GetConfigurationFor(into_message) == B_OK) { fSettings.AddMessage(your_node->Name(), into_message); } return B_OK; } #endif // currently never called by the media kit. Seems it is not implemented. OpenSoundNode * node = dynamic_cast(your_node); if (node == 0) { fprintf(stderr, "<- B_BAD_TYPE\n"); return B_BAD_TYPE; } return node->GetConfigurationFor(into_message); } bool OpenSoundAddOn::WantsAutoStart() { CALLED(); return false; } status_t OpenSoundAddOn::AutoStart( int in_count, BMediaNode ** out_node, int32 * out_internal_id, bool * out_has_more) { CALLED(); return B_OK; } status_t OpenSoundAddOn::RecursiveScan(const char* rootPath, BEntry *rootEntry) { status_t err; int mixer; oss_sysinfo sysinfo; oss_card_info cardinfo; int card, i, j; BList devs; CALLED(); // make sure directories are scanned in this order BDirectory("/dev/audio/hmulti"); BDirectory("/dev/audio/old"); // OSS last, to give precedence to native drivers. // If other addons are loaded first it's ok as well. // Also, we must open it to make sure oss_loader is here, // else we don't get /dev/sndstat since we don't have a symlink in dev/. BDirectory("/dev/oss"); mixer = open(OSS_MIXER_DEV, O_RDWR); if (mixer < 0) { // try to rescan // only works in BeOS BFile fDevFS("/dev/.", B_WRITE_ONLY); const char *drv = "oss_loader"; fDevFS.Write(drv, strlen(drv)); mixer = open(OSS_MIXER_DEV, O_RDWR); if (mixer < 0) { err = errno; goto err0; } } if (ioctl(mixer, SNDCTL_SYSINFO, &sysinfo) < 0) { err = errno; goto err1; } PRINT(("OSS: %s %s (0x%08X)\n", sysinfo.product, sysinfo.version, sysinfo.versionnum)); 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)); /* construct an empty SoundDevice per card */ for (card = 0; card < sysinfo.numcards; card++) { cardinfo.card = card; if (ioctl(mixer, SNDCTL_CARDINFO, &cardinfo) < 0) { err = errno; goto err1; } OpenSoundDevice *device = new OpenSoundDevice(&cardinfo); if (device) devs.AddItem(device); else { err = ENOMEM; goto err1; } } /* Add its audio engines to it */ for (i = 0; i < sysinfo.numaudioengines; i++) { oss_audioinfo audioinfo; audioinfo.dev = i; if (ioctl(mixer, SNDCTL_ENGINEINFO, &audioinfo, sizeof(oss_audioinfo)) < 0) { err = errno; goto err1; } 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)); OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(audioinfo.card_number)); if (device) device->AddEngine(&audioinfo); } /* Add its mixers to it */ for (i = 0; i < sysinfo.nummixers; i++) { oss_mixerinfo mixerinfo; mixerinfo.dev = i; if (ioctl(mixer, SNDCTL_MIXERINFO, &mixerinfo) < 0) { err = errno; goto err1; } PRINT(("OSS: mixer[%d]: card=%d\n", i, mixerinfo.card_number)); OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(mixerinfo.card_number)); if (device) device->AddMixer(&mixerinfo); } /* resolve engine chains of shadow engines */ for (card = 0; card < sysinfo.numcards; card++) { OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(card)); if (!device) continue; for (i = 0; i < device->CountEngines(); i++) { OpenSoundDeviceEngine *engine = device->EngineAt(i); if (engine) { if (engine->Info()->next_play_engine) { for (j = 0; j < device->CountEngines(); j++) { OpenSoundDeviceEngine *next = device->EngineAt(j); if (!next || (engine == next)) continue; if (next->Info()->dev == engine->Info()->next_play_engine) { PRINT(("OSS: engine[%d].next_play = engine[%d]\n", i, j)); engine->fNextPlay = next; break; } } } if (engine->Info()->next_rec_engine) { for (j = 0; j < device->CountEngines(); j++) { OpenSoundDeviceEngine *next = device->EngineAt(j); if (!next || (engine == next)) continue; if (next->Info()->dev == engine->Info()->next_rec_engine) { PRINT(("OSS: engine[%d].next_rec = engine[%d]\n", i, j)); engine->fNextRec = next; break; } } } } } } /* copy correctly initialized devs to fDevices */ for (card = 0; card < sysinfo.numcards; card++) { OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(card)); if (device) { if (card == 0) { /* skip the "oss0" pseudo card device */ delete device; continue; } if ((device->InitDriver() == B_OK) && (device->InitCheck() == B_OK)) fDevices.AddItem(device); else delete device; } } if (fDevices.CountItems()) err = B_OK; else err = ENOENT; err1: close(mixer); err0: return err; #if MA BDirectory root; if (rootEntry != NULL) root.SetTo(rootEntry); else if (rootPath != NULL) { root.SetTo(rootPath); } else { PRINT(("Error in OpenSoundAddOn::RecursiveScan null params\n")); return B_ERROR; } BEntry entry; while (root.GetNextEntry(&entry) > B_ERROR) { if (entry.IsDirectory()) { BPath path; entry.GetPath(&path); OpenSoundDevice *device = new OpenSoundDevice(path.Path() + strlen(rootPath), path.Path()); if (device) { if (device->InitCheck() == B_OK) fDevices.AddItem(device); else delete device; } } } return B_OK; #endif } void OpenSoundAddOn::SaveSettings(void) { CALLED(); BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { path.Append(SETTINGS_FILE); BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); if (file.InitCheck() == B_OK) fSettings.Flatten(&file); } } void OpenSoundAddOn::LoadSettings(void) { CALLED(); fSettings.MakeEmpty(); BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { path.Append(SETTINGS_FILE); BFile file(path.Path(), B_READ_ONLY); if ((file.InitCheck() == B_OK) && (fSettings.Unflatten(&file) == B_OK)) { PRINT_OBJECT(fSettings); } else { PRINT(("Error unflattening settings file %s\n", path.Path())); } } } void OpenSoundAddOn::RegisterMediaFormats(void) { CALLED(); // register non-raw audio formats to the Media Kit #ifdef ENABLE_NON_RAW_SUPPORT OpenSoundDevice::register_media_formats(); #endif }