xref: /haiku/src/add-ons/media/media-add-ons/opensound/OpenSoundAddOn.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
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