1 /* OpenCP Module Player
2  * copyright (c) '94-'10 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3  * copyright (c) '04-'21 Stian Skjelstad <stian.skjelstad@gmail.com>
4  *
5  * Player devices system
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  * revision history: (please note changes here)
22  *  -nb980510   Niklas Beisert <nbeisert@physik.tu-muenchen.de>
23  *    -first release
24  *  -kb980717   Tammo Hinrichs <opencp@gmx.net>
25  *    -changed INI reading of driver symbols to _dllinfo lookup
26  */
27 
28 #include "config.h"
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include "types.h"
33 #include "boot/plinkman.h"
34 #include "boot/psetting.h"
35 #include "devigen.h"
36 #include "deviplay.h"
37 #include "filesel/dirdb.h"
38 #include "filesel/filesystem.h"
39 #include "filesel/filesystem-drive.h"
40 #include "filesel/filesystem-file-mem.h"
41 #include "filesel/filesystem-setup.h"
42 #include "filesel/mdb.h"
43 #include "filesel/pfilesel.h"
44 #include "imsdev.h"
45 #include "player.h"
46 #include "stuff/compat.h"
47 #include "stuff/err.h"
48 
49 int (*plrProcessKey)(uint16_t);
50 
51 static struct devinfonode *plPlayerDevices;
52 static struct devinfonode *curplaydev;
53 static struct devinfonode *defplaydev;
54 static struct interfacestruct plrIntr;
55 static struct preprocregstruct plrPreprocess;
56 
57 int plrBufSize;
58 
59 static struct devinfonode *getdevstr(struct devinfonode *n, const char *hnd)
60 {
61 	while (n)
62 	{
63 		if (!strcasecmp(n->handle, hnd))
64 			return n;
65 		n=n->next;
66 	}
67 	return 0;
68 }
69 
70 static void setdevice(struct devinfonode **curdev, struct devinfonode *dev)
71 {
72 	if (*curdev==dev)
73 		return;
74 	if (*curdev)
75 	{
76 		if ((*curdev)->devinfo.devtype->addprocs)
77 			if ((*curdev)->devinfo.devtype->addprocs->Close)
78 				(*curdev)->devinfo.devtype->addprocs->Close();
79 		plrProcessKey=0;
80 		(*curdev)->devinfo.devtype->Close();
81 		if (!(*curdev)->keep)
82 		{
83 			lnkFree((*curdev)->linkhand);
84 			(*curdev)->linkhand=-1;
85 		}
86 	}
87 	(*curdev)=0;
88 	if (!dev)
89 		return;
90 
91 	if (dev->linkhand<0)
92 	{
93 		char lname[22];
94 		strncpy(lname,cfGetProfileString(dev->handle, "link", ""),21);
95 		dev->linkhand=lnkLink(lname);
96 		if (dev->linkhand<0)
97 		{
98 			fprintf(stderr, "device load error\n");
99 			return;
100 		}
101 		dev->devinfo.devtype=(struct sounddevice *)_lnkGetSymbol(lnkReadInfoReg(dev->linkhand, "driver"));
102 		if (!dev->devinfo.devtype)
103 		{
104 			fprintf(stderr, "device symbol error\n");
105 			lnkFree(dev->linkhand);
106 			dev->linkhand=-1;
107 			return;
108 		}
109 	}
110 
111 	fprintf(stderr, "%s selected...\n", dev->name);
112 	if (dev->devinfo.devtype->Init(&dev->devinfo))
113 	{
114 		if (dev->devinfo.devtype->addprocs)
115 			if (dev->devinfo.devtype->addprocs->Init)
116 				dev->devinfo.devtype->addprocs->Init(dev->handle);
117 		if (dev->devinfo.devtype->addprocs)
118 			if (dev->devinfo.devtype->addprocs->ProcessKey)
119 				plrProcessKey=dev->devinfo.devtype->addprocs->ProcessKey;
120 		(*curdev)=dev;
121 		return;
122 	}
123 	if (*curdev)
124 		if (!(*curdev)->keep)
125 		{
126 			lnkFree((*curdev)->linkhand);
127 			(*curdev)->linkhand=-1;
128 		}
129 	fprintf(stderr, "device init error\n");
130 }
131 
132 static void plrSetDevice(const char *name, int def)
133 {
134 	setdevice(&curplaydev, getdevstr(plPlayerDevices, name));
135 	if (def)
136 		defplaydev=curplaydev;
137 }
138 
139 static void plrResetDevice(void)
140 {
141 	setdevice(&curplaydev, defplaydev);
142 }
143 
144 struct file_devp_t
145 {
146 	struct ocpfile_t head;
147 //	uint32_t filesize;
148 	struct devinfonode *dev;
149 };
150 
151 static void file_devp_ref (struct ocpfile_t *_self)
152 {
153 	struct file_devp_t *self = (struct file_devp_t *)_self;
154 	self->head.refcount++;
155 }
156 static void file_devp_unref (struct ocpfile_t *_self)
157 {
158 	struct file_devp_t *self = (struct file_devp_t *)_self;
159 	self->head.refcount--;
160 	if (!self->head.refcount)
161 	{
162 		dirdbUnref (self->head.dirdb_ref, dirdb_use_file);
163 		free (self);
164 	}
165 }
166 
167 static struct ocpfilehandle_t *file_devp_open (struct ocpfile_t *_self)
168 {
169 	struct file_devp_t *self = (struct file_devp_t *)_self;
170 	char *buffer = strdup (plrIntr.name);
171 	struct ocpfilehandle_t *retval = mem_filehandle_open (/*self->head.parent,*/ self->head.dirdb_ref, buffer, strlen (plrIntr.name));
172 	if (!retval)
173 	{
174 		free (buffer);
175 	}
176 	return retval;
177 }
178 
179 static uint64_t file_devp_filesize (struct ocpfile_t *_self)
180 {
181 	return strlen (plrIntr.name);
182 }
183 
184 static int file_devp_filesize_ready (struct ocpfile_t *_self)
185 {
186 	return 1;
187 }
188 
189 static struct ocpdir_t dir_devp;
190 
191 static void dir_devp_ref (struct ocpdir_t *self)
192 {
193 }
194 static void dir_devp_unref (struct ocpdir_t *self)
195 {
196 }
197 
198 struct dir_devp_handle_t
199 {
200 	void (*callback_file)(void *token, struct ocpfile_t *);
201 	void *token;
202 	struct ocpdir_t *owner;
203 	struct devinfonode *next;
204 };
205 static ocpdirhandle_pt dir_devp_readdir_start (struct ocpdir_t *self, void (*callback_file)(void *token, struct ocpfile_t *),
206 	                                                              void (*callback_dir )(void *token, struct ocpdir_t  *), void *token)
207 {
208 	struct dir_devp_handle_t *retval = malloc (sizeof (*retval));
209 	if (!retval)
210 	{
211 		return 0;
212 	}
213 	retval->callback_file = callback_file;
214 	retval->token = token;
215 	retval->owner = self;
216 	retval->next = plPlayerDevices;
217 	self->ref(self);
218 	return retval;
219 };
220 
221 static void dir_devp_readdir_cancel (ocpdirhandle_pt _handle)
222 {
223 	struct dir_devp_handle_t *handle = (struct dir_devp_handle_t *)_handle;
224 	handle->owner->unref (handle->owner);
225 	free (handle);
226 }
227 
228 static int dir_devp_readdir_iterate (ocpdirhandle_pt _handle)
229 {
230 	struct devinfonode *iter;
231 	struct dir_devp_handle_t *handle = (struct dir_devp_handle_t *)_handle;
232 
233 	for (iter = plPlayerDevices; iter; iter = iter->next)
234 	{
235 		if (handle->next == iter)
236 		{
237 			struct file_devp_t *file = malloc (sizeof (*file));
238 			if (file)
239 			{
240 				char npath[64];
241 				uint32_t mdb_ref;
242 
243 				snprintf (npath, sizeof(npath), "%s.DEV", iter->handle);
244 
245 				ocpfile_t_fill (&file->head,
246 				                 file_devp_ref,
247 				                 file_devp_unref,
248 				                 handle->owner,
249 				                 file_devp_open,
250 				                 file_devp_filesize,
251 				                 file_devp_filesize_ready,
252 				                 dirdbFindAndRef (handle->owner->dirdb_ref, npath, dirdb_use_file),
253 				                 1, /* refcount */
254 				                 1  /* is_nodetect */);
255 				/* file->filesize = iter->devinfo.mem; */
256 				file->dev = iter;
257 
258 				mdb_ref = mdbGetModuleReference2 (file->head.dirdb_ref, strlen (plrIntr.name));
259 				if (mdb_ref!=0xffffffff)
260 				{
261 					struct moduleinfostruct mi;
262 					mdbGetModuleInfo(&mi, mdb_ref);
263 					mi.flags1 &= ~MDB_VIRTUAL;
264 					mi.channels = iter->devinfo.chan;
265 					snprintf (mi.modname, sizeof(mi.modname), "%s", iter->name);
266 					mi.modtype = mtDEVv;
267 					mdbWriteModuleInfo (mdb_ref, &mi);
268 				}
269 				handle->callback_file (handle->token, &file->head);
270 				file->head.unref (&file->head);
271 			}
272 			handle->next = iter->next;
273 			return 1;
274 		}
275 	}
276 	return 0;
277 }
278 
279 static struct ocpdir_t *dir_devp_readdir_dir (struct ocpdir_t *_self, uint32_t dirdb_ref)
280 {
281 	/* this can not succeed */
282 	return 0;
283 }
284 
285 static struct ocpfile_t *dir_devp_readdir_file (struct ocpdir_t *_self, uint32_t dirdb_ref)
286 {
287 	struct devinfonode *iter;
288 	char *searchpath = 0;
289 
290 	uint32_t parent_dirdb_ref;
291 
292 /* assertion begin */
293 	parent_dirdb_ref = dirdbGetParentAndRef (dirdb_ref, dirdb_use_file);
294 	dirdbUnref (parent_dirdb_ref, dirdb_use_file);
295 	if (parent_dirdb_ref != _self->dirdb_ref)
296 	{
297 		fprintf (stderr, "dir_devp_readdir_file: dirdb_ref->parent is not the expected value\n");
298 		return 0;
299 	}
300 /* assertion end */
301 
302 	dirdbGetName_internalstr (dirdb_ref, &searchpath);
303 	if (!searchpath)
304 	{
305 		return 0;
306 	}
307 
308 	for (iter = plPlayerDevices; iter; iter = iter->next)
309 	{
310 		char npath[64];
311 		struct file_devp_t *file;
312 		uint32_t mdb_ref;
313 
314 		snprintf (npath, sizeof(npath), "%s.DEV", iter->handle);
315 
316 		if (strcmp (npath, searchpath))
317 		{
318 			continue;
319 		}
320 
321 		file = malloc (sizeof (*file));
322 		if (!file)
323 		{
324 			fprintf (stderr, "dir_devp_readdir_file: out of memory\n");
325 			return 0;
326 		}
327 
328 		ocpfile_t_fill (&file->head,
329 		                 file_devp_ref,
330 		                 file_devp_unref,
331 		                 _self,
332 		                 file_devp_open,
333 		                 file_devp_filesize,
334 		                 file_devp_filesize_ready,
335 		                 dirdbRef (dirdb_ref, dirdb_use_file),
336 		                 1, /* refcount */
337 		                 1  /* is_nodetect */);
338 
339 		/* file->filesize = iter->devinfo.mem; */
340 		file->dev = iter;
341 
342 		mdb_ref = mdbGetModuleReference2 (file->head.dirdb_ref, strlen (plrIntr.name));
343 		if (mdb_ref!=0xffffffff)
344 		{
345 			struct moduleinfostruct mi;
346 			mdbGetModuleInfo(&mi, mdb_ref);
347 			mi.flags1 &= ~MDB_VIRTUAL;
348 			mi.channels = iter->devinfo.chan;
349 			snprintf (mi.modname, sizeof(mi.modname), "%s", iter->name);
350 			mi.modtype = mtDEVv;
351 			mdbWriteModuleInfo (mdb_ref, &mi);
352 		}
353 
354 		return &file->head;
355 	}
356 	return 0;
357 }
358 
359 static int playdevinited = 0;
360 static int playdevinit(void)
361 {
362 	const char *def;
363 
364 #ifdef INITCLOSE_DEBUG
365 	fprintf(stderr, "playdevinit... trying to init all sound devices [sound]->playerdevices\n");
366 #endif
367 
368 	playdevinited = 1;
369 
370 	plRegisterInterface (&plrIntr);
371 	plRegisterPreprocess (&plrPreprocess);
372 
373 	ocpdir_t_fill (&dir_devp,
374 	                dir_devp_ref,
375 	                dir_devp_unref,
376 	                dmSetup->basedir,
377 	                dir_devp_readdir_start,
378 	                0,
379 	                dir_devp_readdir_cancel,
380 	                dir_devp_readdir_iterate,
381 	                dir_devp_readdir_dir,
382 	                dir_devp_readdir_file,
383 	                0,
384 	                dirdbFindAndRef (dmSetup->basedir->dirdb_ref, "devp", dirdb_use_dir),
385 	                0, /* refcount, not used */
386 	                0, /* is_archive */
387 	                0  /* is_playlist */);
388 	filesystem_setup_register_dir (&dir_devp);
389 
390 	if (!strlen(cfGetProfileString2(cfSoundSec, "sound", "playerdevices", "")))
391 		return errOk;
392 	fprintf(stderr, "playerdevices:\n");
393 	if (!deviReadDevices(cfGetProfileString2(cfSoundSec, "sound", "playerdevices", ""), &plPlayerDevices))
394 	{
395 		fprintf(stderr, "could not install player devices!\n");
396 		return errGen;
397 	}
398 
399 	curplaydev=0;
400 	defplaydev=0;
401 
402 	def=cfGetProfileString("commandline_s", "p", cfGetProfileString2(cfSoundSec, "sound", "defplayer", ""));
403 
404 	if (strlen(def))
405 		plrSetDevice(def, 1);
406 	else
407 		if (plPlayerDevices)
408 			plrSetDevice(plPlayerDevices->handle, 1);
409 
410 	fprintf(stderr, "\n");
411 
412 	plrBufSize=cfGetProfileInt2(cfSoundSec, "sound", "plrbufsize", 100, 10);
413 	if (plrBufSize <= 0)
414 	{
415 		plrBufSize = 1;
416 	}
417 	if (plrBufSize >= 5000)
418 	{
419 		plrBufSize = 5000;
420 	}
421 
422 	if (!curplaydev)
423 	{
424 		fprintf (stderr, "Output device not set\n");
425 		return errGen;
426 	}
427 
428 	return errOk;
429 }
430 
431 static void playdevclose(void)
432 {
433 #ifdef INITCLOSE_DEBUG
434 	fprintf(stderr, "playdevclose...\n");
435 #endif
436 
437 	if (playdevinited)
438 	{
439 		filesystem_setup_unregister_dir (&dir_devp);
440 		dirdbUnref (dir_devp.dirdb_ref, dirdb_use_dir);
441 
442 		plUnregisterInterface (&plrIntr);
443 		plUnregisterPreprocess (&plrPreprocess);
444 
445 		playdevinited = 0;
446 	}
447 
448 	setdevice(&curplaydev, 0);
449 
450 	while (plPlayerDevices)
451 	{
452 		struct devinfonode *o=plPlayerDevices;
453 		plPlayerDevices=plPlayerDevices->next;
454 		free(o);
455 	}
456 }
457 
458 static int plrSetDev(struct moduleinfostruct *mi, struct ocpfilehandle_t *fp)
459 {
460 	char *path, *name;
461 
462 	if (mi->modtype != mtDEVv)
463 	{
464 		return 0;
465 	}
466 
467 	dirdbGetName_internalstr (fp->dirdb_ref, &path);
468 	splitpath4_malloc (path, 0, 0, &name, 0);
469 
470 	plrSetDevice(name, 1);
471 
472 	free (name);
473 
474 	return 0;
475 }
476 
477 
478 static void plrPrep(struct moduleinfostruct *m, struct ocpfilehandle_t **bp)
479 {
480 	plrResetDevice();
481 }
482 
483 static struct interfacestruct plrIntr = {plrSetDev, 0, 0, "plrIntr" INTERFACESTRUCT_TAIL};
484 static struct preprocregstruct plrPreprocess = {plrPrep PREPROCREGSTRUCT_TAIL};
485 #ifndef SUPPORT_STATIC_PLUGINS
486 char *dllinfo = "";
487 #endif
488 DLLEXTINFO_PREFIX struct linkinfostruct dllextinfo = {.name = "plrbase", .desc = "OpenCP Player Devices System (c) 1994-21 Niklas Beisert, Tammo Hinrichs, Stian Skjelstad", .ver = DLLVERSION, .size = 0, .Init = playdevinit, .Close = playdevclose};
489