1 /*
2 audio: audio output interface
3
4 copyright ?-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
5 see COPYING and AUTHORS files in distribution or http://mpg123.org
6 initially written by Michael Hipp
7 */
8
9 #include "out123_int.h"
10 #include "wav.h"
11 #include "hextxt.h"
12 #ifndef NOXFERMEM
13 #include "buffer.h"
have_buffer(out123_handle * ao)14 static int have_buffer(out123_handle *ao)
15 {
16 return (ao->buffer_pid != -1);
17 }
18 #endif
19 #include "stringlists.h"
20
21 #include "debug.h"
22
23 /* An output that is live and does not deal with pausing itself.
24 The device needs to be closed if we stop feeding. */
25 #define SENSITIVE_OUTPUT(ao) \
26 ( (ao)->propflags & OUT123_PROP_LIVE \
27 && !((ao)->propflags & OUT123_PROP_PERSISTENT) )
28
29 static const char *default_name = "out123";
30
modverbose(out123_handle * ao,int final)31 static int modverbose(out123_handle *ao, int final)
32 {
33 mdebug( "modverbose: %x %x %x %d %d"
34 , (unsigned)ao->flags, (unsigned)ao->auxflags, (unsigned)OUT123_QUIET
35 , final, ao->verbose );
36 return final ? (AOQUIET ? 0 : ao->verbose) : -1;
37 }
38
39 static void check_output_module( out123_handle *ao
40 , const char *name, const char *device, int final );
41
out123_clear_module(out123_handle * ao)42 static void out123_clear_module(out123_handle *ao)
43 {
44 ao->open = NULL;
45 ao->get_formats = NULL;
46 ao->write = NULL;
47 ao->flush = NULL;
48 ao->drain = NULL;
49 ao->close = NULL;
50 ao->deinit = NULL;
51 ao->enumerate = NULL;
52
53 ao->module = NULL;
54 ao->userptr = NULL;
55 ao->fn = -1;
56 /* The default is live output devices, files are the special case. */
57 ao->propflags = OUT123_PROP_LIVE;
58 }
59
60 /* Ensure that real name is not leaked, needs to be freed before any call to
61 ao->open(ao). One might free it on closing already, but it might be sensible
62 to keep it around, might still be the same after re-opening. */
aoopen(out123_handle * ao)63 static int aoopen(out123_handle *ao)
64 {
65 if(ao->realname)
66 {
67 free(ao->realname);
68 ao->realname = NULL;
69 }
70 return ao->open(ao);
71 }
72
out123_new(void)73 out123_handle* attribute_align_arg out123_new(void)
74 {
75 out123_handle* ao = malloc( sizeof( out123_handle ) );
76 if(!ao)
77 return NULL;
78 ao->errcode = 0;
79 #ifndef NOXFERMEM
80 ao->buffer_pid = -1;
81 ao->buffer_fd[0] = -1;
82 ao->buffer_fd[1] = -1;
83 ao->buffermem = NULL;
84 #endif
85
86 out123_clear_module(ao);
87 ao->name = compat_strdup(default_name);
88 ao->realname = NULL;
89 ao->driver = NULL;
90 ao->device = NULL;
91
92 ao->flags = OUT123_KEEP_PLAYING;
93 ao->rate = -1;
94 ao->gain = -1;
95 ao->channels = -1;
96 ao->format = -1;
97 ao->framesize = 0;
98 memset(ao->zerosample, 0, 8);
99 ao->state = play_dead;
100 ao->auxflags = 0;
101 ao->preload = 0.;
102 ao->verbose = 0;
103 ao->device_buffer = 0.;
104 ao->bindir = NULL;
105 return ao;
106 }
107
out123_del(out123_handle * ao)108 void attribute_align_arg out123_del(out123_handle *ao)
109 {
110 debug2("[%ld]out123_del(%p)", (long)getpid(), (void*)ao);
111 if(!ao) return;
112
113 out123_close(ao); /* TODO: That talks to the buffer if present. */
114 out123_set_buffer(ao, 0);
115 #ifndef NOXFERMEM
116 if(have_buffer(ao)) buffer_exit(ao);
117 #endif
118 if(ao->name)
119 free(ao->name);
120 if(ao->bindir)
121 free(ao->bindir);
122 free(ao);
123 }
124
out123_free(void * ptr)125 void attribute_align_arg out123_free(void *ptr)
126 {
127 free(ptr);
128 }
129
130 /* Error reporting */
131
132 /* Carefully keep that in sync with the error enum! */
133 /* Sizing according to contents so that we can check! */
134 static const char *const errstring[] =
135 {
136 "no problem"
137 , "out of memory"
138 , "bad driver name"
139 , "failure loading driver module"
140 , "no driver loaded"
141 , "no active audio device"
142 , "some device playback error"
143 , "failed to open device"
144 , "buffer (communication) error"
145 , "basic module system error"
146 , "bad function argument(s)"
147 , "unknown parameter code"
148 , "attempt to set read-only parameter"
149 , "invalid out123 handle"
150 , "operation not supported"
151 , "device enumeration failed"
152 };
153
out123_strerror(out123_handle * ao)154 const char* attribute_align_arg out123_strerror(out123_handle *ao)
155 {
156 return out123_plain_strerror(out123_errcode(ao));
157 }
158
out123_errcode(out123_handle * ao)159 int out123_errcode(out123_handle *ao)
160 {
161 if(!ao) return OUT123_BAD_HANDLE;
162 else return ao->errcode;
163 }
164
out123_plain_strerror(int errcode)165 const char* attribute_align_arg out123_plain_strerror(int errcode)
166 {
167 if(errcode == OUT123_ERR)
168 return "some generic error";
169 if(errcode >= OUT123_ERRCOUNT || errcode < 0)
170 return "invalid error code";
171
172 /* Let's be paranoid, one _may_ forget to extend errstrings when
173 adding a new entry to the enum. */
174 if(errcode >= sizeof(errstring)/sizeof(char*))
175 return "outdated error list (library bug)";
176
177 return errstring[errcode];
178 }
179
out123_seterr(out123_handle * ao,enum out123_error errcode)180 static int out123_seterr(out123_handle *ao, enum out123_error errcode)
181 {
182 if(!ao)
183 return OUT123_ERR;
184 ao->errcode = errcode;
185 return errcode == OUT123_OK ? OUT123_OK : OUT123_ERR;
186 }
187
188 /* pre-playback setup */
189
190 int attribute_align_arg
out123_set_buffer(out123_handle * ao,size_t buffer_bytes)191 out123_set_buffer(out123_handle *ao, size_t buffer_bytes)
192 {
193 debug2("out123_set_buffer(%p, %"SIZE_P")", (void*)ao, (size_p)buffer_bytes);
194 if(!ao)
195 return OUT123_ERR;
196 ao->errcode = 0;
197 /* Close any audio output module if present, also kill of buffer if present,
198 then start new buffer process with newly allocated storage if given
199 size is non-zero. */
200 out123_close(ao);
201 #ifndef NOXFERMEM
202 if(have_buffer(ao))
203 buffer_exit(ao);
204 if(buffer_bytes)
205 return buffer_init(ao, buffer_bytes);
206 #endif
207 return 0;
208 }
209
210 int attribute_align_arg
out123_param2(out123_handle * ao,int code,long value,double fvalue,const char * svalue)211 out123_param2( out123_handle *ao, int code
212 , long value, double fvalue, const char *svalue )
213 {
214 return out123_param(ao, code, value, fvalue, svalue);
215 }
216
217 int attribute_align_arg
out123_param(out123_handle * ao,enum out123_parms code,long value,double fvalue,const char * svalue)218 out123_param( out123_handle *ao, enum out123_parms code
219 , long value, double fvalue, const char *svalue )
220 {
221 int ret = 0;
222
223 debug4("out123_param(%p, %i, %li, %g)", (void*)ao, (int)code, value, fvalue);
224 if(!ao)
225 return OUT123_ERR;
226 ao->errcode = 0;
227
228 switch(code)
229 {
230 case OUT123_FLAGS:
231 ao->flags = (int)value;
232 break;
233 case OUT123_ADD_FLAGS:
234 ao->flags |= (int)value;
235 break;
236 case OUT123_REMOVE_FLAGS:
237 ao->flags &= ~((int)value);
238 break;
239 case OUT123_PRELOAD:
240 ao->preload = fvalue;
241 break;
242 case OUT123_GAIN:
243 ao->gain = value;
244 break;
245 case OUT123_VERBOSE:
246 ao->verbose = (int)value;
247 break;
248 case OUT123_DEVICEBUFFER:
249 ao->device_buffer = fvalue;
250 break;
251 case OUT123_PROPFLAGS:
252 ao->errcode = OUT123_SET_RO_PARAM;
253 ret = OUT123_ERR;
254 break;
255 case OUT123_NAME:
256 if(ao->name)
257 free(ao->name);
258 ao->name = compat_strdup(svalue ? svalue : default_name);
259 break;
260 case OUT123_BINDIR:
261 if(ao->bindir)
262 free(ao->bindir);
263 ao->bindir = compat_strdup(svalue);
264 break;
265 default:
266 ao->errcode = OUT123_BAD_PARAM;
267 if(!AOQUIET) error1("bad parameter code %i", (int)code);
268 ret = OUT123_ERR;
269 }
270 #ifndef NOXFERMEM
271 /* If there is a buffer, it needs to update its copy of parameters. */
272 if(have_buffer(ao))
273 /* No error check; if that fails, buffer is dead and we will notice
274 soon enough. */
275 buffer_sync_param(ao);
276 #endif
277 return ret;
278 }
279
280 int attribute_align_arg
out123_getparam2(out123_handle * ao,int code,long * ret_value,double * ret_fvalue,char ** ret_svalue)281 out123_getparam2( out123_handle *ao, int code
282 , long *ret_value, double *ret_fvalue, char* *ret_svalue )
283 {
284 return out123_getparam(ao, code, ret_value, ret_fvalue, ret_svalue);
285 }
286
287 int attribute_align_arg
out123_getparam(out123_handle * ao,enum out123_parms code,long * ret_value,double * ret_fvalue,char ** ret_svalue)288 out123_getparam( out123_handle *ao, enum out123_parms code
289 , long *ret_value, double *ret_fvalue, char* *ret_svalue )
290 {
291 int ret = 0;
292 long value = 0;
293 double fvalue = 0.;
294 char *svalue = NULL;
295
296 debug4( "out123_getparam(%p, %i, %p, %p)"
297 , (void*)ao, (int)code, (void*)ret_value, (void*)ret_fvalue );
298 if(!ao)
299 return OUT123_ERR;
300 ao->errcode = 0;
301
302 switch(code)
303 {
304 case OUT123_FLAGS:
305 case OUT123_ADD_FLAGS:
306 value = ao->flags;
307 break;
308 case OUT123_PRELOAD:
309 fvalue = ao->preload;
310 break;
311 case OUT123_GAIN:
312 value = ao->gain;
313 break;
314 case OUT123_VERBOSE:
315 value = ao->verbose;
316 break;
317 case OUT123_DEVICEBUFFER:
318 fvalue = ao->device_buffer;
319 break;
320 case OUT123_PROPFLAGS:
321 value = ao->propflags;
322 break;
323 case OUT123_NAME:
324 svalue = ao->realname ? ao->realname : ao->name;
325 break;
326 case OUT123_BINDIR:
327 svalue = ao->bindir;
328 break;
329 default:
330 if(!AOQUIET) error1("bad parameter code %i", (int)code);
331 ao->errcode = OUT123_BAD_PARAM;
332 ret = OUT123_ERR;
333 }
334 if(!ret)
335 {
336 if(ret_value) *ret_value = value;
337 if(ret_fvalue) *ret_fvalue = fvalue;
338 if(ret_svalue) *ret_svalue = svalue;
339 }
340 return ret;
341 }
342
343 int attribute_align_arg
out123_param_from(out123_handle * ao,out123_handle * from_ao)344 out123_param_from(out123_handle *ao, out123_handle* from_ao)
345 {
346 debug2("out123_param_from(%p, %p)", (void*)ao, (void*)from_ao);
347 if(!ao || !from_ao) return -1;
348
349 ao->flags = from_ao->flags;
350 ao->preload = from_ao->preload;
351 ao->gain = from_ao->gain;
352 ao->device_buffer = from_ao->device_buffer;
353 ao->verbose = from_ao->verbose;
354 if(ao->name)
355 free(ao->name);
356 ao->name = compat_strdup(from_ao->name);
357 if(ao->bindir)
358 free(ao->bindir);
359 ao->bindir = compat_strdup(from_ao->bindir);
360
361 return 0;
362 }
363
364 #ifndef NOXFERMEM
365 /* Serialization of tunable parameters to communicate them between
366 main process and buffer. Make sure these two stay in sync ... */
367
write_parameters(out123_handle * ao,int who)368 int write_parameters(out123_handle *ao, int who)
369 {
370 int fd = ao->buffermem->fd[who];
371 if(
372 GOOD_WRITEVAL(fd, ao->flags)
373 && GOOD_WRITEVAL(fd, ao->preload)
374 && GOOD_WRITEVAL(fd, ao->gain)
375 && GOOD_WRITEVAL(fd, ao->device_buffer)
376 && GOOD_WRITEVAL(fd, ao->verbose)
377 && !xfer_write_string(ao, who, ao->name)
378 && !xfer_write_string(ao, who, ao->bindir)
379 )
380 return 0;
381 else
382 return -1;
383 }
384
read_parameters(out123_handle * ao,int who,byte * prebuf,int * preoff,int presize)385 int read_parameters(out123_handle *ao
386 , int who, byte *prebuf, int *preoff, int presize)
387 {
388 int fd = ao->buffermem->fd[who];
389 #define GOOD_READVAL_BUF(fd, val) \
390 !read_buf(fd, &val, sizeof(val), prebuf, preoff, presize)
391 if(
392 GOOD_READVAL_BUF(fd, ao->flags)
393 && GOOD_READVAL_BUF(fd, ao->preload)
394 && GOOD_READVAL_BUF(fd, ao->gain)
395 && GOOD_READVAL_BUF(fd, ao->device_buffer)
396 && GOOD_READVAL_BUF(fd, ao->verbose)
397 && !xfer_read_string(ao, who, &ao->name)
398 && !xfer_read_string(ao, who, &ao->bindir)
399 )
400 return 0;
401 else
402 return -1;
403 #undef GOOD_READVAL_BUF
404 }
405 #endif
406
407 int attribute_align_arg
out123_open(out123_handle * ao,const char * driver,const char * device)408 out123_open(out123_handle *ao, const char* driver, const char* device)
409 {
410 debug4( "[%ld]out123_open(%p, %s, %s)", (long)getpid(), (void*)ao
411 , driver ? driver : "<nil>", device ? device : "<nil>" );
412 if(!ao)
413 return OUT123_ERR;
414 ao->errcode = 0;
415
416 out123_close(ao);
417 debug("out123_open() continuing");
418
419 /* Ensure that audio format is freshly set for "no format yet" mode.
420 In out123_start*/
421 ao->rate = -1;
422 ao->channels = -1;
423 ao->format = -1;
424
425 #ifndef NOXFERMEM
426 if(have_buffer(ao))
427 {
428 if(buffer_open(ao, driver, device))
429 return OUT123_ERR;
430 }
431 else
432 #endif
433 {
434 /* We just quickly check if the device can be accessed at all,
435 same as out123_encodings! */
436 char *nextname, *modnames;
437 const char *names = driver ? driver : DEFAULT_OUTPUT_MODULE;
438
439 if(!names) return out123_seterr(ao, OUT123_BAD_DRIVER_NAME);
440
441 /* It is ridiculous how these error messages are larger than the pieces
442 of memory they are about! */
443 if(device && !(ao->device = compat_strdup(device)))
444 {
445 if(!AOQUIET) error("OOM device name copy");
446 return out123_seterr(ao, OUT123_DOOM);
447 }
448
449 if(!(modnames = compat_strdup(names)))
450 {
451 out123_close(ao); /* Frees ao->device, too. */
452 if(!AOQUIET) error("OOM driver names");
453 return out123_seterr(ao, OUT123_DOOM);
454 }
455
456 /* Now loop over the list of possible modules to find one that works. */
457 nextname = strtok(modnames, ",");
458 while(!ao->open && nextname)
459 {
460 char *curname = nextname;
461 nextname = strtok(NULL, ",");
462 check_output_module(ao, curname, device, !nextname);
463 if(ao->open)
464 {
465 if(AOVERBOSE(2))
466 fprintf(stderr, "Chosen output module: %s\n", curname);
467 /* A bit redundant, but useful when it's a fake module. */
468 if(!(ao->driver = compat_strdup(curname)))
469 {
470 out123_close(ao);
471 if(!AOQUIET) error("OOM driver name");
472 return out123_seterr(ao, OUT123_DOOM);
473 }
474 }
475 }
476
477 free(modnames);
478
479 if(!ao->open) /* At least an open() routine must be present. */
480 {
481 if(!AOQUIET)
482 error2("Found no driver out of [%s] working with device %s."
483 , names, device ? device : "<default>");
484 /* Proper more detailed error code could be set already. */
485 if(ao->errcode == OUT123_OK)
486 ao->errcode = OUT123_BAD_DRIVER;
487 return OUT123_ERR;
488 }
489 }
490 /* Got something. */
491 ao->state = play_stopped;
492 return OUT123_OK;
493 }
494
495 /* Be resilient, always do cleanup work regardless of state. */
out123_close(out123_handle * ao)496 void attribute_align_arg out123_close(out123_handle *ao)
497 {
498 debug2("[%ld]out123_close(%p)", (long)getpid(), (void*)ao);
499 if(!ao)
500 return;
501 ao->errcode = 0;
502
503 out123_drain(ao);
504 out123_stop(ao);
505
506 #ifndef NOXFERMEM
507 if(have_buffer(ao))
508 buffer_close(ao);
509 else
510 #endif
511 {
512 if(ao->deinit)
513 ao->deinit(ao);
514 if(ao->module)
515 close_module(ao->module, modverbose(ao, 0));
516 /* Null module methods and pointer. */
517 out123_clear_module(ao);
518 }
519
520 /* These copies exist in addition to the ones for the buffer. */
521 if(ao->driver)
522 free(ao->driver);
523 ao->driver = NULL;
524 if(ao->device)
525 free(ao->device);
526 ao->device = NULL;
527 if(ao->realname)
528 free(ao->realname);
529 ao->realname = NULL;
530
531 ao->state = play_dead;
532 }
533
534 int attribute_align_arg
out123_start(out123_handle * ao,long rate,int channels,int encoding)535 out123_start(out123_handle *ao, long rate, int channels, int encoding)
536 {
537 debug5( "[%ld]out123_start(%p, %li, %i, %i)", (long)getpid()
538 , (void*)ao, rate, channels, encoding );
539 if(!ao)
540 return OUT123_ERR;
541 ao->errcode = 0;
542
543 out123_stop(ao);
544 debug("out123_start() continuing");
545 if(ao->state != play_stopped)
546 return out123_seterr(ao, OUT123_NO_DRIVER);
547
548 /* Stored right away as parameters for ao->open() and also for reference.
549 framesize needed for out123_play(). */
550 ao->rate = rate;
551 ao->channels = channels;
552 ao->format = encoding;
553 int samplesize = out123_encsize(encoding);
554 ao->framesize = samplesize*channels;
555 // The most convoluted way to say nothing at all.
556 for(int i=0; i<samplesize; ++i)
557 #ifdef WORDS_BIGENDIAN
558 ao->zerosample[samplesize-1-i] =
559 #else
560 ao->zerosample[i] =
561 #endif
562 MPG123_ZEROSAMPLE(ao->format, samplesize, i);
563
564 #ifndef NOXFERMEM
565 if(have_buffer(ao))
566 {
567 if(!buffer_start(ao))
568 {
569 ao->state = play_live;
570 return OUT123_OK;
571 }
572 else
573 return OUT123_ERR;
574 }
575 else
576 #endif
577 {
578 if(aoopen(ao) < 0)
579 return out123_seterr(ao, OUT123_DEV_OPEN);
580 ao->state = play_live;
581 return OUT123_OK;
582 }
583 }
584
out123_pause(out123_handle * ao)585 void attribute_align_arg out123_pause(out123_handle *ao)
586 {
587 debug3( "[%ld]out123_pause(%p) %i", (long)getpid()
588 , (void*)ao, ao ? (int)ao->state : -1 );
589 if(ao && ao->state == play_live)
590 {
591 #ifndef NOXFERMEM
592 if(have_buffer(ao)){ debug("pause with buffer"); buffer_pause(ao); }
593 else
594 #endif
595 {
596 debug1("pause without buffer, sensitive=%d", SENSITIVE_OUTPUT(ao));
597 /* Close live devices to avoid underruns. */
598 if( SENSITIVE_OUTPUT(ao)
599 && ao->close && ao->close(ao) && !AOQUIET )
600 error("trouble closing device");
601 }
602 ao->state = play_paused;
603 }
604 }
605
out123_continue(out123_handle * ao)606 void attribute_align_arg out123_continue(out123_handle *ao)
607 {
608 debug3( "[%ld]out123_continue(%p) %i", (long)getpid()
609 , (void*)ao, ao ? (int)ao->state : -1 );
610 if(ao && ao->state == play_paused)
611 {
612 #ifndef NOXFERMEM
613 if(have_buffer(ao)) buffer_continue(ao);
614 else
615 #endif
616 /* Re-open live devices to avoid underruns. */
617 if(SENSITIVE_OUTPUT(ao) && aoopen(ao) < 0)
618 {
619 /* Will be overwritten by following out123_play() ... */
620 ao->errcode = OUT123_DEV_OPEN;
621 if(!AOQUIET)
622 error("failed re-opening of device after pause");
623 return;
624 }
625 ao->state = play_live;
626 }
627 }
628
out123_stop(out123_handle * ao)629 void attribute_align_arg out123_stop(out123_handle *ao)
630 {
631 debug2("[%ld]out123_stop(%p)", (long)getpid(), (void*)ao);
632 if(!ao)
633 return;
634 ao->errcode = 0;
635 if(!(ao->state == play_paused || ao->state == play_live))
636 return;
637 #ifndef NOXFERMEM
638 if(have_buffer(ao))
639 buffer_stop(ao);
640 else
641 #endif
642 if( ao->state == play_live
643 || (ao->state == play_paused && !SENSITIVE_OUTPUT(ao)) )
644 {
645 if(ao->close && ao->close(ao) && !AOQUIET)
646 error("trouble closing device");
647 }
648 ao->state = play_stopped;
649 }
650
651 // Replace the data in a given block of audio data with zeroes
652 // in the correct encoding.
mute_block(unsigned char * bytes,int count,unsigned char * zerosample,int samplesize)653 static void mute_block( unsigned char *bytes, int count
654 , unsigned char* zerosample, int samplesize )
655 {
656 // The count is expected to be a multiple of samplesize,
657 // this is just to ensure that the loop ends properly, should be noop.
658 count -= count % samplesize;
659 if(!count)
660 return;
661 // Initialize with one zero sample, then multiply that
662 // to eventually cover the whole buffer.
663 memcpy(bytes, zerosample, samplesize);
664 int offset = samplesize;
665 count -= samplesize;
666 while(count)
667 {
668 int block = offset > count ? count : offset;
669 memcpy(bytes+offset, bytes, block);
670 offset += block;
671 count -= block;
672 }
673 }
674
675 size_t attribute_align_arg
out123_play(out123_handle * ao,void * bytes,size_t count)676 out123_play(out123_handle *ao, void *bytes, size_t count)
677 {
678 size_t sum = 0;
679 int written;
680
681 debug5( "[%ld]out123_play(%p, %p, %"SIZE_P") (%i)", (long)getpid()
682 , (void*)ao, bytes, (size_p)count, ao ? (int)ao->state : -1 );
683 if(!ao)
684 return 0;
685 ao->errcode = 0;
686 /* If paused, automatically continue. Other states are an error. */
687 if(ao->state != play_live)
688 {
689 if(ao->state == play_paused)
690 out123_continue(ao);
691 if(ao->state != play_live)
692 {
693 ao->errcode = OUT123_NOT_LIVE;
694 return 0;
695 }
696 }
697
698 /* Ensure that we are writing whole PCM frames. */
699 count -= count % ao->framesize;
700 if(!count) return 0;
701
702 #ifndef NOXFERMEM
703 if(have_buffer(ao))
704 return buffer_write(ao, bytes, count);
705 else
706 #endif
707 {
708 // Write 16K in a piece as maximum, as I've seen random short
709 // writes of big blocks with ALSA.
710 int maxcount = 1<<14;
711 maxcount -= maxcount % ao->framesize;
712 if(maxcount < 1)
713 maxcount = ao->framesize;
714 if(ao->flags & OUT123_MUTE)
715 mute_block( bytes, count, ao->zerosample
716 , MPG123_SAMPLESIZE(ao->format) );
717 do /* Playback in a loop to be able to continue after interruptions. */
718 {
719 errno = 0;
720 int block = count > maxcount ? maxcount : count;
721 written = ao->write(ao, bytes, block);
722 debug4( "written: %d errno: %i (%s), keep_on=%d"
723 , written, errno, strerror(errno)
724 , ao->flags & OUT123_KEEP_PLAYING );
725 if(written > 0)
726 {
727 bytes = (char*)bytes+written;
728 sum += written;
729 count -= written;
730 }
731 if(written < block && errno != EINTR)
732 {
733 ao->errcode = OUT123_DEV_PLAY;
734 if(!AOQUIET)
735 merror( "Error in writing audio, wrote only %d of %d (%s?)!"
736 , written, block, strerror(errno) );
737 /* This is a serious issue ending this playback round. */
738 break;
739 }
740 } while(count && ao->flags & OUT123_KEEP_PLAYING);
741 }
742 debug3( "out123_play(%p, %p, ...) = %"SIZE_P
743 , (void*)ao, bytes, (size_p)sum );
744 return sum;
745 }
746
747 /* Drop means to flush it down. Quickly. */
out123_drop(out123_handle * ao)748 void attribute_align_arg out123_drop(out123_handle *ao)
749 {
750 debug2("[%ld]out123_drop(%p)", (long)getpid(), (void*)ao);
751 if(!ao)
752 return;
753 ao->errcode = 0;
754 #ifndef NOXFERMEM
755 if(have_buffer(ao))
756 buffer_drop(ao);
757 else
758 #endif
759 if(ao->state == play_live)
760 {
761 if(ao->propflags & OUT123_PROP_LIVE && ao->flush)
762 ao->flush(ao);
763 }
764 }
765
out123_drain(out123_handle * ao)766 void attribute_align_arg out123_drain(out123_handle *ao)
767 {
768 debug2("[%ld]out123_drain(%p) ", (long)getpid(), (void*)ao);
769 if(!ao)
770 return;
771 ao->errcode = 0;
772 /* If paused, automatically continue. */
773 if(ao->state != play_live)
774 {
775 if(ao->state == play_paused)
776 out123_continue(ao);
777 if(ao->state != play_live)
778 return;
779 }
780 #ifndef NOXFERMEM
781 if(have_buffer(ao))
782 buffer_drain(ao);
783 else
784 #endif
785 {
786 if(ao->drain)
787 ao->drain(ao);
788 out123_pause(ao);
789 }
790 }
791
out123_ndrain(out123_handle * ao,size_t bytes)792 void attribute_align_arg out123_ndrain(out123_handle *ao, size_t bytes)
793 {
794 debug3("[%ld]out123_ndrain(%p, %"SIZE_P")", (long)getpid(), (void*)ao, (size_p)bytes);
795 if(!ao)
796 return;
797 ao->errcode = 0;
798 /* If paused, automatically continue. */
799 if(ao->state != play_live)
800 {
801 if(ao->state == play_paused)
802 out123_continue(ao);
803 if(ao->state != play_live)
804 return;
805 }
806 #ifndef NOXFERMEM
807 if(have_buffer(ao))
808 buffer_ndrain(ao, bytes);
809 else
810 #endif
811 {
812 if(ao->drain)
813 ao->drain(ao);
814 out123_pause(ao);
815 }
816 }
817
818
819 /* A function that does nothing and returns nothing. */
builtin_nothing(out123_handle * ao)820 static void builtin_nothing(out123_handle *ao){}
test_open(out123_handle * ao)821 static int test_open(out123_handle *ao)
822 {
823 debug("test_open");
824 return OUT123_OK;
825 }
test_get_formats(out123_handle * ao)826 static int test_get_formats(out123_handle *ao)
827 {
828 debug("test_get_formats");
829 return MPG123_ENC_ANY;
830 }
test_write(out123_handle * ao,unsigned char * buf,int len)831 static int test_write(out123_handle *ao, unsigned char *buf, int len)
832 {
833 debug2("test_write: %i B from %p", len, (void*)buf);
834 return len;
835 }
test_flush(out123_handle * ao)836 static void test_flush(out123_handle *ao)
837 {
838 debug("test_flush");
839 }
test_drain(out123_handle * ao)840 static void test_drain(out123_handle *ao)
841 {
842 debug("test_drain");
843 }
test_close(out123_handle * ao)844 static int test_close(out123_handle *ao)
845 {
846 debug("test_drain");
847 return 0;
848 }
849
850 /* Open one of our builtin driver modules. */
open_fake_module(out123_handle * ao,const char * driver)851 static int open_fake_module(out123_handle *ao, const char *driver)
852 {
853 if(!strcmp("test", driver))
854 {
855 ao->propflags &= ~OUT123_PROP_LIVE;
856 ao->open = test_open;
857 ao->get_formats = test_get_formats;
858 ao->write = test_write;
859 ao->flush = test_flush;
860 ao->drain = test_drain;
861 ao->close = test_close;
862 }
863 else
864 if(!strcmp("raw", driver))
865 {
866 ao->propflags &= ~OUT123_PROP_LIVE;
867 ao->open = raw_open;
868 ao->get_formats = raw_formats;
869 ao->write = wav_write;
870 ao->flush = builtin_nothing;
871 ao->drain = wav_drain;
872 ao->close = raw_close;
873 }
874 else
875 if(!strcmp("wav", driver))
876 {
877 ao->propflags &= ~OUT123_PROP_LIVE;
878 ao->open = wav_open;
879 ao->get_formats = wav_formats;
880 ao->write = wav_write;
881 ao->flush = builtin_nothing;
882 ao->drain = wav_drain;
883 ao->close = wav_close;
884 }
885 else
886 if(!strcmp("cdr", driver))
887 {
888 ao->propflags &= ~OUT123_PROP_LIVE;
889 ao->open = cdr_open;
890 ao->get_formats = cdr_formats;
891 ao->write = wav_write;
892 ao->flush = builtin_nothing;
893 ao->drain = wav_drain;
894 ao->close = raw_close;
895 }
896 else
897 if(!strcmp("au", driver))
898 {
899 ao->propflags &= ~OUT123_PROP_LIVE;
900 ao->open = au_open;
901 ao->get_formats = au_formats;
902 ao->write = wav_write;
903 ao->flush = builtin_nothing;
904 ao->drain = wav_drain;
905 ao->close = au_close;
906 }
907 else
908 if(!strcmp("hex", driver))
909 {
910 ao->propflags &= ~OUT123_PROP_LIVE;
911 ao->open = hex_open;
912 ao->get_formats = hex_formats;
913 ao->write = hex_write;
914 ao->flush = builtin_nothing;
915 ao->drain = hextxt_drain;
916 ao->close = hextxt_close;
917 }
918 else
919 if(!strcmp("txt", driver))
920 {
921 ao->propflags &= ~OUT123_PROP_LIVE;
922 ao->open = txt_open;
923 ao->get_formats = txt_formats;
924 ao->write = txt_write;
925 ao->flush = builtin_nothing;
926 ao->drain = hextxt_drain;
927 ao->close = hextxt_close;
928 }
929 else return OUT123_ERR;
930
931 return OUT123_OK;
932 }
933
934 /* Check if given output module is loadable and has a working device.
935 final flag triggers printing and storing of errors. */
check_output_module(out123_handle * ao,const char * name,const char * device,int final)936 static void check_output_module( out123_handle *ao
937 , const char *name, const char *device, int final )
938 {
939 int result;
940
941 debug3("check_output_module %p %p %p", (void*)ao, (void*)device, (void*)ao->device);
942 if(AOVERBOSE(1))
943 fprintf( stderr, "Trying output module: %s, device: %s\n"
944 , name, ao->device ? ao->device : "<nil>" );
945
946 /* Use internal code. */
947 if(open_fake_module(ao, name) == OUT123_OK)
948 return;
949
950 /* Open the module, initial check for availability+libraries. */
951 ao->module = open_module( "output", name, modverbose(ao, final), ao->bindir);
952 if(!ao->module)
953 return;
954 /* Check if module supports output */
955 if(!ao->module->init_output)
956 {
957 if(final && !AOQUIET)
958 error1("Module '%s' does not support audio output.", name);
959 goto check_output_module_cleanup;
960 }
961
962 /* Should I do funny stuff with stderr file descriptor instead? */
963 if(final)
964 {
965 if(AOVERBOSE(2))
966 fprintf(stderr
967 , "Note: %s is the last output option... showing you any error messages now.\n"
968 , name);
969 }
970 else ao->auxflags |= OUT123_QUIET; /* Probing, so don't spill stderr with errors. */
971 result = ao->module->init_output(ao);
972 if(result == 0)
973 { /* Try to open the device. I'm only interested in actually working modules. */
974 ao->format = -1;
975 result = aoopen(ao);
976 debug1("ao->open() = %i", result);
977 if(result >= 0) /* Opening worked, close again. */
978 ao->close(ao);
979 else
980 {
981 if(!AOQUIET)
982 merror("Module '%s' device open failed.", name);
983 if(ao->deinit)
984 ao->deinit(ao); /* Failed, ensure that cleanup after init_output() occurs. */
985 }
986 }
987 else if(!AOQUIET)
988 error2("Module '%s' init failed: %i", name, result);
989
990 ao->auxflags &= ~OUT123_QUIET;
991
992 if(result >= 0)
993 return;
994
995 check_output_module_cleanup:
996 /* Only if module did not check out we get to clean up here. */
997 close_module(ao->module, modverbose(ao, final));
998 out123_clear_module(ao);
999 return;
1000 }
1001
1002 /*
1003 static void audio_output_dump(out123_handle *ao)
1004 {
1005 fprintf(stderr, "ao->fn=%d\n", ao->fn);
1006 fprintf(stderr, "ao->userptr=%p\n", ao->userptr);
1007 fprintf(stderr, "ao->rate=%ld\n", ao->rate);
1008 fprintf(stderr, "ao->gain=%ld\n", ao->gain);
1009 fprintf(stderr, "ao->device='%s'\n", ao->device);
1010 fprintf(stderr, "ao->channels=%d\n", ao->channels);
1011 fprintf(stderr, "ao->format=%d\n", ao->format);
1012 }
1013 */
1014
1015 int attribute_align_arg
out123_drivers(out123_handle * ao,char *** names,char *** descr)1016 out123_drivers(out123_handle *ao, char ***names, char ***descr)
1017 {
1018 char **tmpnames;
1019 char **tmpdescr;
1020 int count;
1021
1022 if(!ao)
1023 return -1;
1024
1025 debug3("out123_drivers(%p, %p, %p)", (void*)ao, (void*)names, (void*)descr);
1026 /* Wrap the call to isolate the lower levels from the user not being
1027 interested in both lists. it's a bit wasteful, but the code looks
1028 ugly enough already down there. */
1029 count = list_modules("output", &tmpnames, &tmpdescr, modverbose(ao, 0), ao->bindir);
1030 debug1("list_modules()=%i", count);
1031 if(count < 0)
1032 {
1033 if(!AOQUIET)
1034 error("Dynamic module search failed.");
1035 count = 0;
1036 }
1037
1038 if(
1039 stringlists_add( &tmpnames, &tmpdescr
1040 , "raw", "raw headerless stream (builtin)", &count )
1041 || stringlists_add( &tmpnames, &tmpdescr
1042 , "cdr", "compact disc digital audio stream (builtin)", &count )
1043 || stringlists_add( &tmpnames, &tmpdescr
1044 , "wav", "RIFF WAVE file (builtin)", &count )
1045 || stringlists_add( &tmpnames, &tmpdescr
1046 , "au", "Sun AU file (builtin)", &count )
1047 || stringlists_add( &tmpnames, &tmpdescr
1048 , "test", "output into the void (builtin)", &count )
1049 || stringlists_add( &tmpnames, &tmpdescr
1050 , "hex", "interleaved hex printout (builtin)", &count )
1051 || stringlists_add( &tmpnames, &tmpdescr
1052 , "txt", "plain text printout, a column per channel (builtin)", &count )
1053 )
1054 if(!AOQUIET)
1055 error("OOM");
1056
1057 /* Return or free gathered lists of names or descriptions. */
1058 if(names)
1059 {
1060 *names = tmpnames;
1061 tmpnames = NULL;
1062 }
1063 if(descr)
1064 {
1065 *descr = tmpdescr;
1066 tmpdescr = NULL;
1067 }
1068 out123_stringlists_free(tmpnames, tmpdescr, count);
1069 return count;
1070 }
1071
1072 struct devlist
1073 {
1074 int count;
1075 char **names;
1076 char **descr;
1077 };
1078
devlist_add(void * dll,const char * name,const char * descr)1079 static int devlist_add(void *dll, const char *name, const char *descr)
1080 {
1081 struct devlist *dl = (struct devlist*)dll;
1082 return dl
1083 ? stringlists_add(&(dl->names), &(dl->descr), name, descr, &(dl->count))
1084 : -1;
1085 }
1086
out123_devices(out123_handle * ao,const char * driver,char *** names,char *** descr,char ** active_driver)1087 int out123_devices( out123_handle *ao, const char *driver, char ***names, char ***descr
1088 , char **active_driver )
1089 {
1090 int ret = 0;
1091 struct devlist dl = {0, NULL, NULL};
1092 char *realdrv = NULL;
1093 debug("");
1094 if(!ao)
1095 return -1;
1096 #ifndef NOXFERMEM
1097 if(have_buffer(ao))
1098 return out123_seterr(ao, OUT123_NOT_SUPPORTED);
1099 #endif
1100
1101 ao->errcode = OUT123_OK;
1102 // If the driver is a single word, not a list with commas.
1103 // Then don't try to open drivers just to know which we are talking about.
1104 if(driver && strchr(driver, ',') == NULL)
1105 realdrv = compat_strdup(driver);
1106 else
1107 {
1108 mdebug("need to find a driver from: %s", driver ? driver : DEFAULT_OUTPUT_MODULE);
1109 if(out123_open(ao, driver, NULL) != OUT123_OK)
1110 return out123_seterr(ao, OUT123_BAD_DRIVER);
1111 mdebug("deduced driver: %s", ao->driver);
1112 realdrv = compat_strdup(ao->driver);
1113 }
1114 if(realdrv == NULL)
1115 return out123_seterr(ao, OUT123_DOOM);
1116
1117 out123_close(ao);
1118
1119 if(open_fake_module(ao, realdrv) != OUT123_OK)
1120 {
1121 ao->module = open_module( "output", realdrv
1122 , modverbose(ao, 0), ao->bindir );
1123 /* Open the module, initial check for availability+libraries. */
1124 if( !ao->module || !ao->module->init_output
1125 || ao->module->init_output(ao) )
1126 ret = out123_seterr(ao, OUT123_BAD_DRIVER);
1127 }
1128
1129 if(!ret && ao->enumerate)
1130 {
1131 if(!ao->enumerate(ao, devlist_add, &dl))
1132 {
1133 ret = dl.count;
1134 if(names)
1135 {
1136 *names = dl.names;
1137 dl.names = NULL;
1138 }
1139 if(descr)
1140 {
1141 *descr = dl.descr;
1142 dl.descr = NULL;
1143 }
1144 if(active_driver)
1145 {
1146 *active_driver = realdrv;
1147 realdrv = NULL;
1148 }
1149 } else
1150 ret = out123_seterr(ao, OUT123_DEV_ENUMERATE);
1151 out123_stringlists_free(dl.names, dl.descr, dl.count);
1152 if(ao->deinit)
1153 ao->deinit(ao);
1154 } else if(!ret)
1155 ret = out123_seterr(ao, OUT123_NOT_SUPPORTED);
1156
1157 free(realdrv);
1158 if(ao->module)
1159 close_module(ao->module, modverbose(ao, 0));
1160 out123_clear_module(ao);
1161 return ret;
1162 }
1163
1164 /* We always have ao->driver and ao->device set, also with buffer.
1165 The latter can be positively NULL, though. */
1166 int attribute_align_arg
out123_driver_info(out123_handle * ao,char ** driver,char ** device)1167 out123_driver_info(out123_handle *ao, char **driver, char **device)
1168 {
1169 debug3( "out123_driver_info(%p, %p, %p)"
1170 , (void*)ao, (void*)driver, (void*)device );
1171 if(!ao)
1172 return OUT123_ERR;
1173 if(!ao->driver)
1174 return out123_seterr(ao, OUT123_NO_DRIVER);
1175
1176 if(driver)
1177 *driver = ao->driver;
1178 if(device)
1179 *device = ao->device;
1180 return OUT123_OK;
1181 }
1182
1183 int attribute_align_arg
out123_encodings(out123_handle * ao,long rate,int channels)1184 out123_encodings(out123_handle *ao, long rate, int channels)
1185 {
1186 debug4( "[%ld]out123_encodings(%p, %li, %i)", (long)getpid()
1187 , (void*)ao, rate, channels );
1188 if(!ao)
1189 return OUT123_ERR;
1190 ao->errcode = OUT123_OK;
1191
1192 out123_stop(ao); /* That brings the buffer into waiting state, too. */
1193
1194 if(ao->state != play_stopped)
1195 return out123_seterr(ao, OUT123_NO_DRIVER);
1196
1197 ao->channels = channels;
1198 ao->rate = rate;
1199 #ifndef NOXFERMEM
1200 if(have_buffer(ao))
1201 return buffer_encodings(ao);
1202 else
1203 #endif
1204 {
1205 int enc = 0;
1206 /* This tells outputs to choose a fitting format so that ao->open() succeeds
1207 They possibly set a sample rate and channel count they like best.
1208 We should add API to retrieve those defaults, too. */
1209 ao->format = -1;
1210 if(aoopen(ao) >= 0)
1211 {
1212 /* Need to reset those since the choose-your-format open
1213 call might have changed them. */
1214 ao->channels = channels;
1215 ao->rate = rate;
1216 enc = ao->get_formats(ao);
1217 ao->close(ao);
1218 return enc;
1219 }
1220 else
1221 return out123_seterr(ao, (ao->errcode != OUT123_OK
1222 ? ao->errcode
1223 : OUT123_DEV_OPEN));
1224 }
1225 }
1226
out123_encsize(int encoding)1227 int attribute_align_arg out123_encsize(int encoding)
1228 {
1229 return MPG123_SAMPLESIZE(encoding);
1230 }
1231
1232 int attribute_align_arg
out123_formats(out123_handle * ao,const long * rates,int ratecount,int minchannels,int maxchannels,struct mpg123_fmt ** fmtlist)1233 out123_formats( out123_handle *ao, const long *rates, int ratecount
1234 , int minchannels, int maxchannels
1235 , struct mpg123_fmt **fmtlist )
1236 {
1237 debug7( "[%ld]out123_formats(%p, %p, %i, %i, %i, %p)", (long)getpid()
1238 , (void*)ao, (void*)rates, ratecount, minchannels, maxchannels
1239 , (void*)fmtlist );
1240 if(!ao)
1241 return OUT123_ERR;
1242 ao->errcode = OUT123_OK;
1243
1244 out123_stop(ao); /* That brings the buffer into waiting state, too. */
1245
1246 if(ao->state != play_stopped)
1247 return out123_seterr(ao, OUT123_NO_DRIVER);
1248
1249 if(ratecount > 0 && !rates)
1250 return out123_seterr(ao, OUT123_ARG_ERROR);
1251 if(!fmtlist || minchannels > maxchannels)
1252 return out123_seterr(ao, OUT123_ARG_ERROR);
1253 *fmtlist = NULL; /* Initialize so free(fmtlist) is always allowed. */
1254
1255 #ifndef NOXFERMEM
1256 if(have_buffer(ao))
1257 return buffer_formats( ao, rates, ratecount
1258 , minchannels, maxchannels, fmtlist );
1259 else
1260 #endif
1261 {
1262 /* This tells outputs to choose a fitting format so that ao->open()
1263 succeeds. */
1264 ao->format = -1;
1265 ao->rate = -1;
1266 ao->channels = -1;
1267 if(aoopen(ao) >= 0)
1268 {
1269 struct mpg123_fmt *fmts;
1270 int ri, ch;
1271 int fi = 0;
1272 int fmtcount = 1; /* Always the default format. */
1273 if(ratecount > 0)
1274 fmtcount += ratecount*(maxchannels-minchannels+1);
1275 if(!(fmts = malloc(sizeof(*fmts)*fmtcount)))
1276 {
1277 ao->close(ao);
1278 return out123_seterr(ao, OUT123_DOOM);
1279 }
1280 /* Store default format if present. */
1281 if(ao->format > 0 && ao->channels > 0 && ao->rate > 0)
1282 {
1283 fmts[0].rate = ao->rate;
1284 fmts[0].channels = ao->channels;
1285 fmts[0].encoding = ao->format;
1286 }
1287 else
1288 { /* Ensure consistent -1 in all entries. */
1289 fmts[0].rate = -1;
1290 fmts[0].channels = -1;
1291 fmts[0].encoding = -1;
1292 }
1293 /* Test all combinations of rate and channel count. */
1294 for(ri=0; ri<ratecount; ++ri)
1295 for(ch=minchannels; ch<=maxchannels; ++ch)
1296 {
1297 ++fi;
1298 ao->rate = rates[ri];
1299 ao->channels = ch;
1300 fmts[fi].rate = ao->rate;
1301 fmts[fi].channels = ao->channels;
1302 fmts[fi].encoding = ao->get_formats(ao);
1303 }
1304 ao->close(ao);
1305
1306 *fmtlist = fmts;
1307 return fmtcount;
1308 }
1309 else
1310 return out123_seterr(ao, (ao->errcode != OUT123_OK
1311 ? ao->errcode
1312 : OUT123_DEV_OPEN));
1313 }
1314 }
1315
1316
out123_buffered(out123_handle * ao)1317 size_t attribute_align_arg out123_buffered(out123_handle *ao)
1318 {
1319 debug2("[%ld]out123_buffered(%p)", (long)getpid(), (void*)ao);
1320 if(!ao)
1321 return 0;
1322 ao->errcode = 0;
1323 #ifndef NOXFERMEM
1324 if(have_buffer(ao))
1325 {
1326 size_t fill = buffer_fill(ao);
1327 debug2("out123_buffered(%p) = %"SIZE_P, (void*)ao, (size_p)fill);
1328 return fill;
1329 }
1330 else
1331 #endif
1332 return 0;
1333 }
1334
out123_getformat(out123_handle * ao,long * rate,int * channels,int * encoding,int * framesize)1335 int attribute_align_arg out123_getformat( out123_handle *ao
1336 , long *rate, int *channels, int *encoding, int *framesize )
1337 {
1338 if(!ao)
1339 return OUT123_ERR;
1340
1341 if(!(ao->state == play_paused || ao->state == play_live))
1342 return out123_seterr(ao, OUT123_NOT_LIVE);
1343
1344 if(rate)
1345 *rate = ao->rate;
1346 if(channels)
1347 *channels = ao->channels;
1348 if(encoding)
1349 *encoding = ao->format;
1350 if(framesize)
1351 *framesize = ao->framesize;
1352 return OUT123_OK;
1353 }
1354
1355 struct enc_desc
1356 {
1357 int code; /* MPG123_ENC_SOMETHING */
1358 const char *longname; /* signed bla bla */
1359 const char *name; /* sXX, short name */
1360 };
1361
1362 static const struct enc_desc encdesc[] =
1363 {
1364 { MPG123_ENC_SIGNED_16, "signed 16 bit", "s16" }
1365 , { MPG123_ENC_UNSIGNED_16, "unsigned 16 bit", "u16" }
1366 , { MPG123_ENC_SIGNED_32, "signed 32 bit", "s32" }
1367 , { MPG123_ENC_UNSIGNED_32, "unsigned 32 bit", "u32" }
1368 , { MPG123_ENC_SIGNED_24, "signed 24 bit", "s24" }
1369 , { MPG123_ENC_UNSIGNED_24, "unsigned 24 bit", "u24" }
1370 , { MPG123_ENC_FLOAT_32, "float (32 bit)", "f32" }
1371 , { MPG123_ENC_FLOAT_64, "float (64 bit)", "f64" }
1372 , { MPG123_ENC_SIGNED_8, "signed 8 bit", "s8" }
1373 , { MPG123_ENC_UNSIGNED_8, "unsigned 8 bit", "u8" }
1374 , { MPG123_ENC_ULAW_8, "mu-law (8 bit)", "ulaw" }
1375 , { MPG123_ENC_ALAW_8, "a-law (8 bit)", "alaw" }
1376 };
1377 #define KNOWN_ENCS (sizeof(encdesc)/sizeof(struct enc_desc))
1378
out123_enc_list(int ** enclist)1379 int attribute_align_arg out123_enc_list(int **enclist)
1380 {
1381 int i;
1382 if(!enclist)
1383 return OUT123_ERR;
1384 *enclist = malloc(sizeof(int)*KNOWN_ENCS);
1385 if(!(*enclist))
1386 return OUT123_ERR;
1387 for(i=0;i<KNOWN_ENCS;++i)
1388 (*enclist)[i] = encdesc[i].code;
1389 return KNOWN_ENCS;
1390 }
1391
out123_enc_byname(const char * name)1392 int attribute_align_arg out123_enc_byname(const char *name)
1393 {
1394 int i;
1395 if(!name)
1396 return OUT123_ERR;
1397 for(i=0; i<KNOWN_ENCS; ++i) if(
1398 !strcasecmp(encdesc[i].name, name)
1399 || !strcasecmp(encdesc[i].longname, name)
1400 )
1401 return encdesc[i].code;
1402 return OUT123_ERR;
1403 }
1404
out123_enc_name(int encoding)1405 const char* attribute_align_arg out123_enc_name(int encoding)
1406 {
1407 int i;
1408 for(i=0; i<KNOWN_ENCS; ++i) if(encdesc[i].code == encoding)
1409 return encdesc[i].name;
1410 return NULL;
1411 }
1412
out123_enc_longname(int encoding)1413 const char* attribute_align_arg out123_enc_longname(int encoding)
1414 {
1415 int i;
1416 for(i=0; i<KNOWN_ENCS; ++i) if(encdesc[i].code == encoding)
1417 return encdesc[i].longname;
1418 return NULL;
1419 }
1420