* 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 <MediaDefs.h>
#include <MediaAddOn.h>
#include <Errors.h>
#include <Node.h>
#include <Mime.h>
#include <StorageDefs.h>
#include <Path.h>
#include <Directory.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Debug.h>
#include <errno.h>
#include "OpenSoundNode.h"
#include "OpenSoundDevice.h"
#include "OpenSoundDeviceEngine.h"
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "debug.h"
#define MULTI_SAVE
extern "C" _EXPORT BMediaAddOn * make_media_addon(image_id image) {
CALLED();
return new OpenSoundAddOn(image);
}
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;
if (RecursiveScan("/dev/oss/") != B_OK)
return;
if (RecursiveScan("/dev/audio/oss/") != B_OK)
return;
*/
LoadSettings();
fInitCheckStatus = B_OK;
}
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<OpenSoundNode*>(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
OpenSoundNode * node = dynamic_cast<OpenSoundNode*>(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();
BDirectory("/dev/audio/hmulti");
BDirectory("/dev/audio/old");
BDirectory("/dev/oss");
mixer = open(OSS_MIXER_DEV, O_RDWR);
if (mixer < 0) {
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));
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;
}
}
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);
}
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);
}
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;
}
}
}
}
}
}
for (card = 0; card < sysinfo.numcards; card++) {
OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(card));
if (device) {
if (card == 0) {
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();
#ifdef ENABLE_NON_RAW_SUPPORT
OpenSoundDevice::register_media_formats();
#endif
}