Tizen 2.0 Release
[platform/upstream/libremix.git] / src / libremix / remix_base.c
1 /*
2  * libremix -- An audio mixing and sequencing library.
3  *
4  * Copyright (C) 2001 Commonwealth Scientific and Industrial Research
5  * Organisation (CSIRO), Australia.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 /*
24  * RemixBase: A generic interface for sound generation and processing.
25  *
26  * Conrad Parker <conrad@metadecks.org>, August 2001
27  */
28
29 #include <string.h>
30
31 #define __REMIX__
32 #include "remix.h"
33
34
35 RemixBase *
36 remix_new (RemixEnv * env, RemixPlugin * plugin, CDSet * parameters)
37 {
38   RemixBase * base;
39
40   if (plugin == RemixNone) {
41     remix_set_error (env, REMIX_ERROR_NOENTITY);
42     return RemixNone;
43   }
44
45   base = remix_base_new (env);
46
47   if (base == RemixNone) {
48     remix_set_error (env, REMIX_ERROR_NOENTITY);
49     return RemixNone;
50   }
51
52   base->plugin = plugin;
53
54   if (plugin->init != NULL) {
55     if (plugin->init (env, base, parameters) == RemixNone) {
56       remix_destroy (env, base);
57       return RemixNone;
58     }
59   }
60
61   return base;
62 }
63
64 CDSet *
65 remix_suggest (RemixEnv * env, RemixPlugin * plugin, CDSet * parameters)
66 {
67   if (plugin == RemixNone) {
68     remix_set_error (env, REMIX_ERROR_NOENTITY);
69     return RemixNone;
70   }
71
72   if (!plugin->suggest) {
73     /* XXX: should remix_set_error: is NOOP appropriate ?? */
74     return RemixNone;
75   }
76
77   return plugin->suggest (env, plugin, parameters, plugin->plugin_data);
78 }
79
80 static int
81 remix_parameter_scheme_get_key (RemixEnv * env, CDSet * scheme_set, char * name)
82 {
83   CDSet * s;
84   RemixParameterScheme * scheme;
85
86   for (s = scheme_set; s; s = s->next) {
87     scheme = (RemixParameterScheme *)s->data.s_pointer;
88     if (!strcmp (scheme->name, name)) return s->key;
89   }
90
91   remix_set_error (env, REMIX_ERROR_NOENTITY);
92
93   return -1;
94 }
95
96 int
97 remix_get_init_parameter_key (RemixEnv * env, RemixPlugin * plugin, char * name)
98 {
99   if (plugin == RemixNone) {
100     remix_dprintf ("[remix_get_init_parameter_key] plugin == RemixNone\n");
101     remix_set_error (env, REMIX_ERROR_NOENTITY);
102     return -1;
103   }
104
105   return remix_parameter_scheme_get_key (env, plugin->init_scheme, name);
106 }
107
108 int
109 remix_get_parameter_key (RemixEnv * env, RemixBase * base, char * name)
110 {
111   if (base == RemixNone) {
112     remix_dprintf ("[remix_get_parameter_key] base == RemixNone\n");
113     remix_set_error (env, REMIX_ERROR_NOENTITY);
114     return -1;
115   }
116
117   if (base->plugin == RemixNone) {
118     remix_dprintf ("[remix_get_parameter_key] base->plugin == RemixNone\n");
119     remix_set_error (env, REMIX_ERROR_NOENTITY);
120     return -1;
121   }
122
123   return remix_parameter_scheme_get_key (env, base->plugin->process_scheme, name);
124 }
125
126 RemixParameter
127 remix_set_parameter (RemixEnv * env, RemixBase * base, int key,
128                     RemixParameter parameter)
129 {
130   if (base == RemixNone) {
131     remix_dprintf ("[remix_set_parameter] base == RemixNone\n");
132     remix_set_error (env, REMIX_ERROR_NOENTITY);
133     return (RemixParameter)-1;
134   }
135
136   remix_dprintf ("[remix_set_parameter] base %p, [%d] ==> %p\n", base, key,
137                  parameter.s_pointer);
138   base->parameters = cd_set_replace (env, base->parameters, key, parameter);
139   return parameter;
140 }
141
142 RemixParameter
143 remix_get_parameter (RemixEnv * env, RemixBase * base, int key)
144 {
145   RemixParameter p;
146
147   if (base == RemixNone) {
148     remix_dprintf ("[remix_get_parameter] base == RemixNone\n");
149     remix_set_error (env, REMIX_ERROR_NOENTITY);
150     return (RemixParameter)-1;
151   }
152
153   p = cd_set_find (env, base->parameters, key);
154   remix_dprintf ("[remix_get_parameter] base %p, [%d] == %p\n", base, key,
155                  p.s_pointer);
156   return p;
157 }
158
159 RemixParameterType
160 remix_get_parameter_type (RemixEnv * env, RemixBase * base, int key)
161 {
162   RemixPlugin * plugin;
163   CDScalar k;
164   RemixParameterScheme * scheme;
165
166   if (base == RemixNone) {
167     remix_dprintf ("[remix_get_parameter_type] base == RemixNone\n");
168     remix_set_error (env, REMIX_ERROR_NOENTITY);
169     return -1;
170   }
171
172   plugin = base->plugin;
173
174   if (plugin == RemixNone) {
175     remix_dprintf ("[remix_get_parameter_type] base->plugin == RemixNone\n");
176     remix_set_error (env, REMIX_ERROR_NOENTITY);
177     return -1;
178   }
179
180   k = cd_set_find (env, plugin->process_scheme, key);
181   scheme = (RemixParameterScheme *)k.s_pointer;
182
183   if (scheme == RemixNone) {
184     remix_dprintf ("[remix_get_parameter_type] scheme == RemixNone\n");
185     remix_set_error (env, REMIX_ERROR_NOENTITY);
186     return -1;
187   }
188
189   return scheme->type;
190 }
191
192 RemixBase *
193 remix_base_new_subclass (RemixEnv * env, size_t size)
194 {
195   RemixBase * base = remix_malloc (size);
196   _remix_context_copy (env, &base->context_limit);
197   _remix_register_base (env, base);
198   return base;
199 }
200
201 RemixBase *
202 remix_base_new (RemixEnv * env)
203 {
204   return remix_base_new_subclass (env, sizeof (struct _RemixBase));
205 }
206
207 RemixCount
208 remix_base_get_mixlength (RemixEnv * env, RemixBase * base)
209 {
210   if (base == RemixNone) {
211     remix_set_error (env, REMIX_ERROR_NOENTITY);
212     return -1;
213   }
214
215   return _remix_base_get_mixlength (env, base);
216 }
217
218 RemixSamplerate
219 remix_base_get_samplerate (RemixEnv * env, RemixBase * base)
220 {
221   if (base == RemixNone) {
222     remix_set_error (env, REMIX_ERROR_NOENTITY);
223     return -1;
224   }
225
226   return _remix_base_get_samplerate (env, base);
227 }
228
229 RemixTempo
230 remix_base_get_tempo (RemixEnv * env, RemixBase * base)
231 {
232   if (base == RemixNone) {
233     remix_set_error (env, REMIX_ERROR_NOENTITY);
234     return -1;
235   }
236
237   return _remix_base_get_tempo (env, base);
238 }
239
240 CDSet *
241 remix_base_get_channels (RemixEnv * env, RemixBase * base)
242 {
243   if (base == RemixNone) {
244     remix_set_error (env, REMIX_ERROR_NOENTITY);
245     return RemixNone;
246   }
247
248   return _remix_base_get_channels (env, base);
249 }
250
251 void *
252 remix_base_set_instance_data (RemixEnv * env, RemixBase * base, void * data)
253 {
254   if (base == RemixNone) {
255     remix_set_error (env, REMIX_ERROR_NOENTITY);
256     return RemixNone;
257   }
258
259   return _remix_set_instance_data (env, base, data);
260 }
261
262 void *
263 remix_base_get_instance_data (RemixEnv * env, RemixBase * base)
264 {
265   if (base == RemixNone) {
266     remix_set_error (env, REMIX_ERROR_NOENTITY);
267     return RemixNone;
268   }
269
270   return _remix_get_instance_data (env, base);
271 }
272
273 int
274 remix_base_has_samplerate (RemixEnv * env, RemixBase * base)
275 {
276   RemixSamplerate asr, bsr;
277
278   asr = remix_get_samplerate (env);
279   bsr = _remix_base_get_samplerate (env, base);
280
281   return (asr == bsr);
282 }
283
284 int
285 remix_base_has_tempo (RemixEnv * env, RemixBase * base)
286 {
287   RemixTempo at, bt;
288
289   at = remix_get_tempo (env);
290   bt = _remix_base_get_tempo (env, base);
291
292   return (at == bt);
293 }
294
295 int
296 remix_base_encompasses_mixlength (RemixEnv * env, RemixBase * base)
297 {
298   RemixCount aml, bml;
299
300   aml = remix_get_mixlength (env);
301   bml = _remix_base_get_mixlength (env, base);
302
303   return (aml < bml);
304 }
305
306 int
307 remix_base_encompasses_channels (RemixEnv * env, RemixBase * base)
308 {
309   CDSet * s, * as, * bs;
310   CDScalar k;
311
312   as = remix_get_channels (env);
313   bs = _remix_base_get_channels (env, base);
314
315   for (s = as; s; s = s->next) {
316     k = cd_set_find (env, bs, s->key);
317     if (k.s_pointer == NULL) return 0;
318   }
319
320   return 1;
321 }
322
323 RemixMethods *
324 remix_base_set_methods (RemixEnv * env, RemixBase * base, RemixMethods * methods)
325 {
326   RemixMethods * old = base->methods;
327   _remix_set_methods (env, base, methods);
328   return old;
329 }
330
331 RemixMethods *
332 remix_base_get_methods (RemixEnv * env, RemixBase * base)
333 {
334   return _remix_get_methods (env, base);
335 }
336
337 RemixPlugin *
338 remix_base_set_plugin (RemixEnv * env, RemixBase * base, RemixPlugin * plugin)
339 {
340   RemixPlugin * old = base->plugin;
341   _remix_set_plugin (env, base, plugin);
342   return old;
343 }
344
345 RemixPlugin *
346 remix_base_get_plugin (RemixEnv * env, RemixBase * base)
347 {
348   return _remix_get_plugin (env, base);
349 }
350
351 RemixBase *
352 remix_clone_subclass (RemixEnv * env, RemixBase * base)
353 {
354   if (!base) {
355     remix_set_error (env, REMIX_ERROR_NOENTITY);
356     return NULL;
357   }
358   if (!base->methods || !base->methods->clone) {
359     remix_set_error (env, REMIX_ERROR_INVALID);
360     return NULL;
361   }
362   return _remix_clone (env, base);
363 }
364
365 int
366 remix_destroy (RemixEnv * env, RemixBase * base)
367 {
368   if (!base) {
369     remix_set_error (env, REMIX_ERROR_NOENTITY);
370     return -1;
371   }
372
373   _remix_unregister_base (env, base);
374
375   if (!base->methods || !base->methods->destroy) {
376     remix_set_error (env, REMIX_ERROR_INVALID);
377     return -1;
378   }
379   return _remix_destroy (env, base);
380 }
381
382 int
383 remix_destroy_list (RemixEnv * env, CDList * list)
384 {
385   cd_list_destroy_with (env, list, (CDDestroyFunc)remix_destroy);
386   return 0;
387 }
388
389 /*
390  * Prepare the methods for process, seek and length calls.
391  *
392  * "Prepare" means to make sure the base has enough internal buffers
393  * to deal with the current context (sample rate, mixlength).
394  *
395  * If the methods has a ready() function, that is checked first. If it
396  * does not have a ready() function, it is assumed to always be ready.
397  */
398 RemixBase *
399 remix_prepare (RemixEnv * env, RemixBase * base)
400 {
401   int is_ready = 0;
402
403   if (!base) {
404     remix_set_error (env, REMIX_ERROR_NOENTITY);
405     return NULL;
406   }
407   if (base->methods && base->methods->prepare) {
408     if (base->methods->ready) {
409       is_ready = base->methods->ready (env, base);
410     }
411
412     _remix_context_merge (env, &base->context_limit);
413
414     if (!is_ready) base =_remix_prepare (env, base);
415   }
416
417   return base;
418 }
419
420 RemixCount
421 remix_process_fast (RemixEnv * env, RemixBase * base, RemixCount count,
422                    RemixStream * input, RemixStream * output)
423 {
424   RemixCount n;
425
426   if (!base) {
427     remix_set_error (env, REMIX_ERROR_NOENTITY);
428     return -1;
429   }
430   if (!base->methods || !base->methods->process) {
431     remix_set_error (env, REMIX_ERROR_INVALID);
432     return -1;
433   }
434   n = _remix_process (env, base, count, input, output);
435   if (n > 0) base->offset += n;
436   return n;
437 }
438
439 RemixCount
440 remix_process (RemixEnv * env, RemixBase * base, RemixCount count,
441               RemixStream * input, RemixStream * output)
442 {
443   RemixCount processed;
444   RemixCount n;
445   RemixError error;
446   char * str;
447
448   remix_debug_down ();
449
450   processed = remix_process_fast (env, base, count, input, output);
451
452   if (processed == -1) {
453     error = remix_last_error (env);
454     str = remix_error_string (env, error);
455     remix_dprintf ("*** ERROR in remix_process: %s\n", str);
456     switch (error) {
457     case REMIX_ERROR_NOOP:
458       n = remix_stream_write (env, output, count, input);
459       if (n > 0) {
460         base->offset += n;
461         processed = n;
462       }
463       break;
464     case REMIX_ERROR_SILENCE:
465       n = remix_stream_write0 (env, output, count);
466       if (n > 0) {
467         base->offset += n;
468         processed = n;
469       }
470       break;
471     default: break;
472     }
473   }
474
475   remix_debug_up ();
476
477   return processed;
478 }
479
480 RemixCount
481 remix_length (RemixEnv * env, RemixBase * base)
482 {
483   if (!base) {
484     remix_set_error (env, REMIX_ERROR_NOENTITY);
485     return -1;
486   }
487   if (!base->methods || !base->methods->length) {
488     remix_set_error (env, REMIX_ERROR_INVALID);
489     return -1;
490   }
491   return _remix_length (env, base);
492 }
493
494 RemixCount
495 remix_seek (RemixEnv * env, RemixBase * base, RemixCount offset, int whence)
496 {
497   RemixCount new_offset, len;
498
499   if (!base) {
500     remix_set_error (env, REMIX_ERROR_NOENTITY);
501     return -1;
502   }
503
504   new_offset = base->offset;
505
506   switch (whence) {
507   case SEEK_SET: new_offset = offset; break;
508   case SEEK_CUR: new_offset += offset; break;
509   case SEEK_END:
510     len = remix_length (env, base);
511     if (len == -1) return -1;
512     new_offset = len + offset;
513     break;
514   default:
515     remix_set_error (env, REMIX_ERROR_INVALID);
516     return -1;
517     break;
518   }
519
520   if (new_offset == base->offset) return new_offset;
521
522   remix_dprintf ("SEEK %p @ %ld\n", base, new_offset);
523
524   if (base->methods && base->methods->seek)
525     base->offset = base->methods->seek (env, base, new_offset);
526   else
527     base->offset = new_offset;
528
529   return base->offset;
530 }
531
532 RemixCount
533 remix_tell (RemixEnv * env, RemixBase * base)
534 {
535   if (!base) {
536     remix_set_error (env, REMIX_ERROR_NOENTITY);
537     return -1;
538   }
539   return base->offset;
540 }
541
542 int
543 remix_flush (RemixEnv * env, RemixBase * base)
544 {
545   if (!base) {
546     remix_set_error (env, REMIX_ERROR_NOENTITY);
547     return -1;
548   }
549   if (!base->methods || !base->methods->flush) {
550     remix_set_error (env, REMIX_ERROR_INVALID);
551     return -1;
552   }
553   return _remix_flush (env, base);
554 }
555
556 RemixMetaText *
557 remix_get_meta_text (RemixEnv * env, RemixBase * base)
558 {
559   RemixPlugin * plugin = base->plugin;
560   return (plugin ? plugin->metatext : NULL);
561 }
562
563 RemixMetaText *
564 remix_set_meta_text (RemixEnv * env, RemixBase * base, RemixMetaText * mt)
565 {
566   RemixPlugin * plugin;
567   RemixMetaText * old;
568
569   if (base == RemixNone) {
570     remix_set_error (env, REMIX_ERROR_NOENTITY);
571     return RemixNone;
572   }
573
574   plugin = base->plugin;
575   if (plugin == RemixNone) {
576     remix_set_error (env, REMIX_ERROR_NOENTITY);
577     return RemixNone;
578   }
579
580   old = plugin->metatext;
581   plugin->metatext = mt;
582   return old;
583 }
584
585 int
586 remix_is_writeable (RemixEnv * env, RemixBase * base)
587 {
588   RemixPlugin * plugin;
589
590   if (base == RemixNone) {
591     remix_set_error (env, REMIX_ERROR_NOENTITY);
592     return -1;
593   }
594
595   plugin = base->plugin;
596   if (plugin == RemixNone) {
597     remix_set_error (env, REMIX_ERROR_NOENTITY);
598     return -1;
599   }
600
601   return (plugin->flags & REMIX_PLUGIN_WRITEABLE);
602 }
603
604 int
605 remix_is_seekable (RemixEnv * env, RemixBase * base)
606 {
607   RemixPlugin * plugin;
608
609   if (base == RemixNone) {
610     remix_set_error (env, REMIX_ERROR_NOENTITY);
611     return -1;
612   }
613
614   plugin = base->plugin;
615   if (plugin == RemixNone) {
616     remix_set_error (env, REMIX_ERROR_NOENTITY);
617     return -1;
618   }
619
620   return (plugin->flags & REMIX_PLUGIN_SEEKABLE);
621 }
622
623 int
624 remix_is_cacheable (RemixEnv * env, RemixBase * base)
625 {
626   RemixPlugin * plugin;
627
628   if (base == RemixNone) {
629     remix_set_error (env, REMIX_ERROR_NOENTITY);
630     return -1;
631   }
632
633   plugin = base->plugin;
634   if (plugin == RemixNone) {
635     remix_set_error (env, REMIX_ERROR_NOENTITY);
636     return -1;
637   }
638
639   return (plugin->flags & REMIX_PLUGIN_CACHEABLE);
640 }
641
642 int
643 remix_is_causal (RemixEnv * env, RemixBase * base)
644 {
645   RemixPlugin * plugin;
646
647   if (base == RemixNone) {
648     remix_set_error (env, REMIX_ERROR_NOENTITY);
649     return -1;
650   }
651
652   plugin = base->plugin;
653   if (plugin == RemixNone) {
654     remix_set_error (env, REMIX_ERROR_NOENTITY);
655     return -1;
656   }
657
658   return (plugin->flags & REMIX_PLUGIN_CAUSAL);
659 }