xref: /haiku/src/add-ons/media/media-add-ons/opensound/OpenSoundAddOn.cpp (revision 3be9edf8da228afd9fec0390f408c964766122aa)
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(("%d 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(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 	mixer = open(OSS_MIXER_DEV, O_RDWR);
208 	if (mixer < 0) {
209 		// try to rescan
210 		BFile fDevFS("/dev/.", B_WRITE_ONLY);
211 		const char *drv = "oss_loader";
212 		fDevFS.Write(drv, strlen(drv));
213 		mixer = open(OSS_MIXER_DEV, O_RDWR);
214 		if (mixer < 0) {
215 			err = errno;
216 			goto err0;
217 		}
218 	}
219 
220 	if (ioctl(mixer, SNDCTL_SYSINFO, &sysinfo) < 0) {
221 		err = errno;
222 		goto err1;
223 	}
224 
225 	PRINT(("OSS: %s %s (0x%08X)\n", sysinfo.product, sysinfo.version, sysinfo.versionnum));
226 	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));
227 
228 	/* construct an empty SoundDevice per card */
229 
230 	for (card = 0; card < sysinfo.numcards; card++) {
231 		cardinfo.card = card;
232 		if (ioctl(mixer, SNDCTL_CARDINFO, &cardinfo) < 0) {
233 			err = errno;
234 			goto err1;
235 		}
236 		OpenSoundDevice *device = new OpenSoundDevice(&cardinfo);
237 		if (device)
238 			devs.AddItem(device);
239 		else {
240 			err = ENOMEM;
241 			goto err1;
242 		}
243 	}
244 
245 	/* Add its audio engines to it */
246 
247 	for (i = 0; i < sysinfo.numaudioengines; i++) {
248 		oss_audioinfo audioinfo;
249 		audioinfo.dev = i;
250 		if (ioctl(mixer, SNDCTL_ENGINEINFO, &audioinfo, sizeof(oss_audioinfo)) < 0) {
251 			err = errno;
252 			goto err1;
253 		}
254 		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));
255 		OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(audioinfo.card_number));
256 		if (device)
257 			device->AddEngine(&audioinfo);
258 	}
259 
260 	/* Add its mixers to it */
261 
262 	for (i = 0; i < sysinfo.nummixers; i++) {
263 		oss_mixerinfo mixerinfo;
264 		mixerinfo.dev = i;
265 		if (ioctl(mixer, SNDCTL_MIXERINFO, &mixerinfo) < 0) {
266 			err = errno;
267 			goto err1;
268 		}
269 		PRINT(("OSS: mixer[%d]: card=%d\n", i, mixerinfo.card_number));
270 		OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(mixerinfo.card_number));
271 		if (device)
272 			device->AddMixer(&mixerinfo);
273 	}
274 
275 	/* resolve engine chains of shadow engines */
276 
277 	for (card = 0; card < sysinfo.numcards; card++) {
278 		OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(card));
279 		if (!device)
280 			continue;
281 		for (i = 0; i < device->CountEngines(); i++) {
282 			OpenSoundDeviceEngine *engine = device->EngineAt(i);
283 			if (engine) {
284 				if (engine->Info()->next_play_engine) {
285 					for (j = 0; j < device->CountEngines(); j++) {
286 						OpenSoundDeviceEngine *next = device->EngineAt(j);
287 						if (!next || (engine == next))
288 							continue;
289 						if (next->Info()->dev == engine->Info()->next_play_engine) {
290 							PRINT(("OSS: engine[%d].next_play = engine[%d]\n", i, j));
291 							engine->fNextPlay = next;
292 							break;
293 						}
294 					}
295 				}
296 				if (engine->Info()->next_rec_engine) {
297 					for (j = 0; j < device->CountEngines(); j++) {
298 						OpenSoundDeviceEngine *next = device->EngineAt(j);
299 						if (!next || (engine == next))
300 							continue;
301 						if (next->Info()->dev == engine->Info()->next_rec_engine) {
302 							PRINT(("OSS: engine[%d].next_rec = engine[%d]\n", i, j));
303 							engine->fNextRec = next;
304 							break;
305 						}
306 					}
307 				}
308 			}
309 		}
310 	}
311 
312 	/* copy correctly initialized devs to fDevices */
313 
314 	for (card = 0; card < sysinfo.numcards; card++) {
315 		OpenSoundDevice *device = (OpenSoundDevice *)(devs.ItemAt(card));
316 		if (device) {
317 			if (card == 0) { /* skip the "oss0" pseudo card device */
318 				delete device;
319 				continue;
320 			}
321 			if ((device->InitDriver() == B_OK) && (device->InitCheck() == B_OK))
322 				fDevices.AddItem(device);
323 			else
324 				delete device;
325 		}
326 	}
327 	if (fDevices.CountItems())
328 		err = B_OK;
329 	else
330 		err = ENOENT;
331 err1:
332 	close(mixer);
333 err0:
334 	return err;
335 
336 #if MA
337 	BDirectory root;
338 	if (rootEntry != NULL)
339 		root.SetTo(rootEntry);
340 	else if (rootPath != NULL) {
341 		root.SetTo(rootPath);
342 	} else {
343 		PRINT(("Error in OpenSoundAddOn::RecursiveScan null params\n"));
344 		return B_ERROR;
345 	}
346 
347 	BEntry entry;
348 
349 	while (root.GetNextEntry(&entry) > B_ERROR) {
350 
351 		if (entry.IsDirectory()) {
352 			BPath path;
353 			entry.GetPath(&path);
354 			OpenSoundDevice *device = new OpenSoundDevice(path.Path() + strlen(rootPath), path.Path());
355 			if (device) {
356 				if (device->InitCheck() == B_OK)
357 					fDevices.AddItem(device);
358 				else
359 					delete device;
360 			}
361 		}
362 	}
363 
364 	return B_OK;
365 #endif
366 }
367 
368 
369 void
370 OpenSoundAddOn::SaveSettings(void)
371 {
372 	CALLED();
373 	BPath path;
374 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
375 		path.Append(SETTINGS_FILE);
376 		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
377 		if (file.InitCheck() == B_OK)
378 			fSettings.Flatten(&file);
379 	}
380 }
381 
382 
383 void
384 OpenSoundAddOn::LoadSettings(void)
385 {
386 	CALLED();
387 	fSettings.MakeEmpty();
388 
389 	BPath path;
390 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
391 		path.Append(SETTINGS_FILE);
392 		BFile file(path.Path(), B_READ_ONLY);
393 		if ((file.InitCheck() == B_OK) && (fSettings.Unflatten(&file) == B_OK))
394 		{
395 			PRINT_OBJECT(fSettings);
396 		} else {
397 			PRINT(("Error unflattening settings file %s\n", path.Path()));
398 		}
399 	}
400 }
401 
402 
403 void
404 OpenSoundAddOn::RegisterMediaFormats(void)
405 {
406 	CALLED();
407 	// register non-raw audio formats to the Media Kit
408 #ifdef ENABLE_NON_RAW_SUPPORT
409 	OpenSoundDevice::register_media_formats();
410 #endif
411 
412 
413 }
414