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