Tscope5
audio.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // __ ______
4 // / /_______________ ____ ___ / ____/
5 // / __/ ___/ ___/ __ \/ __ \/ _ \ /___ )
6 // / /_(__ ) /__/ /_/ / /_/ / __/ ____/ /
7 // \__/____/\___/\____/ .___/\___/ /_____/
8 // /_/
9 //
10 /// \file audio.c
11 /// Definitions of audio functions
12 /// \example audio01.c
13 ////////////////////////////////////////////////////////////////////////////////
14 
15 
16 ////////////////////////////////////////////////////////////////////////////////
17 /// @name Audio parameters
18 /// The parameters of the audio output device (sound card) can be set
19 /// using the functions below.
20 ///
21 /// The audio gain (amplification factor) can be changed at any time.
22 /// All other functions functions in this group have to be called before
23 /// the audio subsystem is loaded
24 /// (i.e. before the first sample is loaded and played)
25 /// and their settings remain unchanged until the end of the program.
26 ///
27 /// The parameter functions also define the settings for samples that
28 /// are created with ts5_alloc_sample().
29 //@{
30 ////////////////////////////////////////////////////////////////////////////////
31 
32 #include "../include/tscope5/audio.h"
33 #include "../include/tscope5/audio_internal.h"
34 #include "../include/tscope5/system_internal.h"
35 
36 
37 ////////////////////////////////////////////////////////////////////////////////
38 /// Set the requested sample rate for the audio system.
39 ///
40 /// \param frequency The sample rate. Can be TS5_11025, TS5_22050 or TS5_44100.
41 ///
42 /// \return The previous requested sample rate.
43 ///
44 /// This function has to be called before the (automatic)
45 /// installation of the audio system.
46 ///
47 /// Once the audio system is installed,
48 /// the sample rate of the system cannot be changed.
49 ///
50 /// This is only a request, the audio driver can choose non-matching values.
51 /// Query the audio system after installation with ts5_get_audio_frequency()
52 /// for the actual sample rate.
53 ///
54 /// This function also influences the sample rate of samples made by
55 /// ts5_alloc_sample().
56 ///
57 /// The default frequency is TS5_44100.
58 ////////////////////////////////////////////////////////////////////////////////
59 unsigned int ts5_set_audio_frequency(unsigned int frequency)
60 {
62  ts5_install_tscope5("ts5_set_audio_frequency");
63  }
64 
65  ts5_log(TS5_LOGLEVEL_4, "ts5_set_audio_frequency(%u)\n", frequency);
66 
67  unsigned int retval = _ts5_status.audio.frequency;
68 
69  if (frequency!=TS5_11025 && frequency!=TS5_22050 && frequency!=TS5_44100) {
70  ts5_fatal("ts5_set_audio_frequency: unknown frequency requested\n");
71  }
72 
73  _ts5_status.audio.frequency = frequency;
74 
75  ts5_log(TS5_LOGLEVEL_4,
76  "ts5_set_audio_frequency: set sample rate to %u (was %u)\n",
77  _ts5_status.audio.frequency, retval);
78 
79  return retval;
80 }
81 
82 
83 ////////////////////////////////////////////////////////////////////////////////
84 /// Get the sample rate of the audio system.
85 ///
86 /// \return The sample rate of the audio system as an unsigned integer.
87 ////////////////////////////////////////////////////////////////////////////////
89 {
90  ts5_check_audio("ts5_get_audio_frequency");
91  ts5_log(TS5_LOGLEVEL_4, "ts5_get_audio_frequency()\n");
92 
93  return al_get_voice_frequency(_ts5_data.audio.voice);
94 }
95 
96 
97 ////////////////////////////////////////////////////////////////////////////////
98 /// Set the requested number of channels for the audio system.
99 ///
100 /// \param channels The number of channels. Can be TS5_MONO or TS5_STEREO.
101 ///
102 /// \return The previous requested number of channels.
103 ///
104 /// This function has to be called before the (automatic)
105 /// installation of the audio system.
106 ///
107 /// Once the audio system is installed,
108 /// the number of channels of the system cannot be changed.
109 ///
110 /// This is only a request, the audio driver can choose non-matching values.
111 /// Query the audio system after installation with ts5_get_audio_channels()
112 /// for the actual number of channels.
113 ///
114 /// This function also influences the number of channels of samples
115 /// made by ts5_alloc_sample().
116 ///
117 /// The default number of channels is TS5_STEREO.
118 ////////////////////////////////////////////////////////////////////////////////
119 unsigned int ts5_set_audio_channels(unsigned int channels)
120 {
122  ts5_install_tscope5("ts5_set_audio_channels");
123  }
124 
125  ts5_log(TS5_LOGLEVEL_4, "ts5_set_audio_channels(%d)\n", channels);
126 
127  unsigned int retval = _ts5_status.audio.channels;
128 
129  if (channels!=TS5_MONO && channels!=TS5_STEREO) {
130  ts5_fatal("%s: %s\n", "ts5_set_audio_channels",
131  "unknown number of channels requested");
132  }
133 
134  _ts5_status.audio.channels = channels;
135 
136  ts5_log(TS5_LOGLEVEL_4, "%s: %s %u (was %u)\n",
137  "ts5_set_audio_channels",
138  "set number of channels to",
139  _ts5_status.audio.channels, retval);
140 
141  return retval;
142 }
143 
144 
145 ////////////////////////////////////////////////////////////////////////////////
146 /// Get the number of channels of the audio system.
147 ///
148 /// \return The number of channels of the audio system.
149 ////////////////////////////////////////////////////////////////////////////////
151 {
152  ts5_check_audio("ts5_get_audio_channels");
153  ts5_log(TS5_LOGLEVEL_4, "ts5_get_audio_channels()\n");
154 
155  ALLEGRO_CHANNEL_CONF channels =
156  al_get_voice_channels(_ts5_data.audio.voice);
157 
158  unsigned int retval;
159 
160  if (channels == ALLEGRO_CHANNEL_CONF_1) {
161  retval = TS5_MONO;
162  }
163  else if (channels == ALLEGRO_CHANNEL_CONF_2) {
164  retval = TS5_STEREO;
165  }
166  else {
167  ts5_fatal("%s: %s\n", "ts5_get_audio_channels",
168  "got an unknown number of channels");
169  retval = 0;
170  }
171 
172  return retval;
173 }
174 
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 /// Set the sample depth for the audio system.
178 ///
179 /// \param depth The sample depth. Can be TS5_INTEGER or TS5_FLOAT.
180 ///
181 /// \return The previous requested sample depth.
182 ///
183 /// This function has to be called before the (automatic)
184 /// installation of the audio system.
185 ///
186 /// Once the audio system is installed,
187 /// the sample depth of the system cannot be changed.
188 ///
189 /// This is only a request, the audio driver can choose non-matching values.
190 /// Query the audio system after installation with ts5_get_audio_depth()
191 /// for the actual sample depth.
192 ///
193 /// This function also influences the sample depth of samples
194 /// made by ts5_alloc_sample().
195 ///
196 /// The default sample depth is TS5_FLOAT.
197 ///
198 /// \todo currently has no effect.
199 /// The voice will be integer and the mixer floating point.
200 ////////////////////////////////////////////////////////////////////////////////
201 int ts5_set_audio_depth(int depth)
202 {
204  ts5_install_tscope5("ts5_set_audio_depth");
205  }
206 
207  ts5_log(TS5_LOGLEVEL_4, "ts5_set_audio_depth(%d)\n", depth);
208 
209  int retval = _ts5_status.audio.depth;
210 
211  if (depth!=TS5_INTEGER && depth!=TS5_FLOAT) {
212  ts5_fatal("ts5_set_audio_depth: unknown sample depth requested\n");
213  }
214 
215  _ts5_status.audio.depth = depth;
216 
217  ts5_log(TS5_LOGLEVEL_4, "%s: %s %d (was %d)\n",
218  "ts5_set_audio_depth", "set sample depth to",
219  _ts5_status.audio.depth, retval);
220 
221  return retval;
222 }
223 
224 
225 ////////////////////////////////////////////////////////////////////////////////
226 /// Get the sample depth of the audio system.
227 ///
228 /// \return The sample depth of the audio system.
229 ////////////////////////////////////////////////////////////////////////////////
231 {
232  ts5_check_audio("ts5_get_audio_depth");
233  ts5_log(TS5_LOGLEVEL_4, "ts5_get_audio_depth()\n");
234 
235  ALLEGRO_AUDIO_DEPTH depth =
236  al_get_voice_depth(_ts5_data.audio.voice);
237 
238  if (depth != TS5_INTEGER && depth !=TS5_FLOAT) {
239  ts5_fatal("ts5_get_audio_depth: got an unknown sample depth\n");
240  }
241 
242  return depth;
243 }
244 
245 
246 ////////////////////////////////////////////////////////////////////////////////
247 /// Set the gain (amplification factor) of the audio system.
248 ///
249 /// \param gain The gain.
250 /// 0.0 = silent, 0.5=half, 1.0 = untouched, 2.0 = double, ...
251 ///
252 /// \return The previous gain.
253 ///
254 /// The default gain is 1.0.
255 ////////////////////////////////////////////////////////////////////////////////
256 double ts5_set_audio_gain(double gain)
257 {
259  ts5_install_tscope5("ts5_set_audio_gain");
260  }
261 
262  ts5_log(TS5_LOGLEVEL_4, "ts5_set_audio_gain(%f)\n", gain);
263 
264  double retval = _ts5_status.audio.gain;
265 
266  if (gain<0.0) {
267  ts5_fatal("ts5_set_audio_gain: gain should be >= 0.0\n");
268  }
269 
270  _ts5_status.audio.gain = gain;
271 
272  if (!al_set_mixer_gain(_ts5_data.audio.mixer, gain)) {
273  ts5_fatal("ts5_set_audio_gain: could not change audio gain\n");
274  }
275 
276  ts5_log(TS5_LOGLEVEL_4, "ts5_set_audio_gain: set gain to %f (was %f)\n",
277  _ts5_status.audio.gain, retval);
278 
279  return retval;
280 }
281 
282 
283 ////////////////////////////////////////////////////////////////////////////////
284 /// Get the gain of the audio system.
285 ///
286 /// \return The gain of the audio system.
287 ////////////////////////////////////////////////////////////////////////////////
289 {
290  ts5_check_audio("ts5_get_audio_gain");
291  ts5_log(TS5_LOGLEVEL_4, "ts5_get_audio_gain()\n");
292 
293  return _ts5_status.audio.gain;
294 }
295 
296 
297 ////////////////////////////////////////////////////////////////////////////////
298 //@}
299 ////////////////////////////////////////////////////////////////////////////////
300 
301 
302 ////////////////////////////////////////////////////////////////////////////////
303 /// @name Loading samples
304 /// Samples can either be read from a file, or empty samples can be generated.
305 ///
306 /// Empty samples can be used for recording vocal responses (tbd).
307 ///
308 /// Most samples (samples that contain less than 2^24 sample values)
309 /// can be read completely into memory using ts5_read_sample().
310 /// Longer samples should be loaded with ts5_read_long_sample().
311 /// These will be streamed
312 /// (i.e. they will be divided in parts that are read when necessary).
313 //@{
314 ////////////////////////////////////////////////////////////////////////////////
315 
316 
317 ////////////////////////////////////////////////////////////////////////////////
318 /// Create an empty sample.
319 ///
320 /// \param length Length of the sample in seconds.
321 ///
322 /// \return A pointer to the newly created sample.
323 ///
324 /// Sample frequency, channels and depth are set by ts5_set_audio_frequency(),
325 /// ts5_set_audio_channels() and ts5_set_audio_depth()
326 ////////////////////////////////////////////////////////////////////////////////
327 TS5_SAMPLE *ts5_alloc_sample(double length)
328 {
329  ts5_check_audio("ts5_alloc_sample");
330  ts5_log(TS5_LOGLEVEL_2, "ts5_alloc_sample(%f)\n", length);
331 
332  unsigned int samples = length * _ts5_status.audio.frequency;
333 
334  ALLEGRO_CHANNEL_CONF channels;
335 
336  if (_ts5_status.audio.channels==TS5_MONO) {
337  channels=ALLEGRO_CHANNEL_CONF_1;
338  }
339  else if (_ts5_status.audio.channels==TS5_STEREO) {
340  channels=ALLEGRO_CHANNEL_CONF_2;
341  }
342  else {
343  ts5_fatal("%s: %s\n", "ts5_alloc_sample",
344  "unknown channel configuration requested");
345  }
346 
347  unsigned int sample_size =
348  al_get_channel_count(channels) *
349  al_get_audio_depth_size(_ts5_status.audio.depth);
350 
351  unsigned int bytes = samples * sample_size;
352 
353  void *buff = NULL;
354  buff = al_malloc(bytes);
355 
356  if (!buff) {
357  ts5_fatal("ts5_alloc_sample: could not allocate sample buffer\n");
358  }
359 
360  TS5_SAMPLE *sample = NULL;
361  sample = (TS5_SAMPLE *)al_malloc(sizeof(TS5_SAMPLE));
362 
363  if (!sample) {
364  ts5_fatal("ts5_alloc_sample: could not allocate sample\n");
365  }
366 
367  sample->sample = al_create_sample(buff, samples,
368  _ts5_status.audio.frequency,
369  _ts5_status.audio.depth, channels, true);
370 
371  if (!sample->sample) {
372  ts5_fatal("ts5_alloc_sample: could not create sample\n");
373  }
374 
375  sample->instance = al_create_sample_instance(NULL);
376 
377  if (!sample->instance) {
378  ts5_fatal("ts5_alloc_sample: could not create sample instance\n");
379  }
380 
381  if (!al_set_sample(sample->instance, sample->sample)) {
382  ts5_fatal("%s: %s\n", "ts5_alloc_sample",
383  "could not attach sample to sample instance");
384  }
385 
386  if (!al_attach_sample_instance_to_mixer
387  (sample->instance, _ts5_data.audio.mixer)) {
388  ts5_fatal("%s: %s\n", "ts5_alloc_sample",
389  "could not attach sample instance to mixer");
390  }
391 
392  al_set_sample_instance_playing(sample->instance, false);
393 
394  sample->issample = true;
395  sample->isfilestream = false;
396  sample->ismemorystream = false;
397  sample->stream = NULL;
398 
399  return sample;
400 }
401 
402 
403 ////////////////////////////////////////////////////////////////////////////////
404 /// Open a sample from a file.
405 ///
406 /// \param file Path to the sample file.
407 ///
408 /// \return A pointer to the newly created sample.
409 ///
410 /// Available sample types are: wav, flac, ogg, it, mod, s3m, xm.
411 ///
412 /// The file is read from disk all at once.
413 ////////////////////////////////////////////////////////////////////////////////
414 TS5_SAMPLE *ts5_read_sample(const char *file)
415 {
416  ts5_check_audio("ts5_read_sample");
417  ts5_log(TS5_LOGLEVEL_2, "ts5_read_sample(%s)\n", file);
418 
419  TS5_SAMPLE *sample = NULL;
420  sample = (TS5_SAMPLE *)al_malloc(sizeof(TS5_SAMPLE));
421 
422  if (!sample) {
423  ts5_fatal("ts5_read_sample: could not allocate sample\n");
424  }
425 
426  sample->issample = true;
427  sample->isfilestream = false;
428  sample->ismemorystream = false;
429 
430  sample->sample = NULL;
431  sample->sample = al_load_sample(file);
432 
433  if (!sample->sample) {
434  ts5_fatal("ts5_read_sample: could not read sample\n");
435  }
436 
437  sample->instance = NULL;
438  sample->instance = al_create_sample_instance(NULL);
439 
440  if (!sample->instance) {
441  ts5_fatal("ts5_read_sample: could not create sample instance\n");
442  }
443 
444  if (!al_set_sample(sample->instance, sample->sample)) {
445  ts5_fatal("%s: %s\n", "ts5_read_sample",
446  "could not attach sample to sample instance");
447  }
448 
449  if (!al_attach_sample_instance_to_mixer
450  (sample->instance, _ts5_data.audio.mixer)) {
451  ts5_fatal("%s: %s\n", "ts5_read_sample",
452  "could not attach sample instance to mixer");
453  }
454 
455  al_set_sample_instance_playing(sample->instance, false);
456 
457  sample->stream = NULL;
458 
459  return sample;
460 }
461 
462 
463 ////////////////////////////////////////////////////////////////////////////////
464 /// Open a long sample from a file.
465 ///
466 /// \param file Path to the sample file.
467 ///
468 /// \return A pointer to the newly created sample.
469 ///
470 /// Available sample types are: wav, flac, ogg, it, mod, s3m, xm.
471 ///
472 /// The file is read from disk as it is needed.
473 ////////////////////////////////////////////////////////////////////////////////
474 TS5_SAMPLE *ts5_read_long_sample(const char *file)
475 {
476  ts5_check_audio("ts5_read_long_sample");
477  ts5_log(TS5_LOGLEVEL_2, "ts5_read_long_sample(%s)\n", file);
478 
479  TS5_SAMPLE *sample = NULL;
480  sample = (TS5_SAMPLE *)al_malloc(sizeof(TS5_SAMPLE));
481 
482  if (!sample) {
483  ts5_fatal("ts5_read_long_sample: could not allocate sample\n");
484  }
485 
486  sample->issample = false;
487  sample->isfilestream = true;
488  sample->ismemorystream = false;
489 
490  sample->sample = NULL;
491  sample->instance = NULL;
492 
493  sample->stream = NULL;
494  sample->stream = al_load_audio_stream(file, 4, 2048);
495 
496  if (!sample->stream) {
497  ts5_fatal("ts5_read_long_sample: could not read sample\n");
498  }
499 
500  if (!al_attach_audio_stream_to_mixer
501  (sample->stream, _ts5_data.audio.mixer)) {
502  ts5_fatal("%s: %s\n", "ts5_read_long_sample",
503  "could not attach sample stream to mixer");
504  }
505 
506  al_set_audio_stream_playing(sample->stream, false);
507  al_set_audio_stream_playmode(sample->stream, ALLEGRO_PLAYMODE_ONCE);
508 
509  return sample;
510 }
511 
512 
513 ////////////////////////////////////////////////////////////////////////////////
514 /// Write a sample.
515 ///
516 /// \param file Path to the sample file.
517 /// \param sample Pointer to the sample that will be written.
518 ///
519 /// Only .wav is supported. The file extension should be .wav
520 ////////////////////////////////////////////////////////////////////////////////
521 void ts5_write_sample(const char *file, TS5_SAMPLE *sample)
522 {
523  ts5_check_audio("ts5_write_sample");
524  ts5_log(TS5_LOGLEVEL_2, "ts5_write_sample(%s,%p)\n", file, sample);
525 
526  if (!sample->issample) {
527  ts5_fatal("%s: %s\n", "ts5_write_sample",
528  "writing of streams is not implemented yet\n");
529  }
530 
531  if (!al_save_sample(file, sample->sample)) {
532  ts5_fatal("ts5_write_sample: could not write sample\n");
533  }
534 }
535 
536 
537 ////////////////////////////////////////////////////////////////////////////////
538 /// Free the memory used by a sample.
539 ///
540 /// \param sample Pointer to the sample that will be freed.
541 ///
542 /// This function should be called at the end of the program for
543 /// each sample allocated or read by the user.
544 ////////////////////////////////////////////////////////////////////////////////
545 void ts5_free_sample(TS5_SAMPLE *sample)
546 {
547  ts5_check_audio("ts5_free_sample");
548  ts5_log(TS5_LOGLEVEL_2, "ts5_free_sample(%p)\n", sample);
549 
550  if (sample->issample) {
551  al_set_sample(sample->instance, NULL);
552  al_destroy_sample(sample->sample);
553 
554  al_detach_sample_instance(sample->instance);
555  al_destroy_sample_instance(sample->instance);
556  }
557  else if (sample->isfilestream) {
558  al_detach_audio_stream(sample->stream);
559  al_destroy_audio_stream(sample->stream);
560  }
561  else if (sample->ismemorystream) {
562  ts5_fatal("ts5_free_sample: memory streams not implemented yet\n");
563  }
564 
565  al_free(sample);
566 }
567 
568 
569 ////////////////////////////////////////////////////////////////////////////////
570 //@}
571 ////////////////////////////////////////////////////////////////////////////////
572 
573 
574 ////////////////////////////////////////////////////////////////////////////////
575 /// @name Playing samples
576 /// Audio samples can be played using the functions below.
577 ///
578 /// Playing a sample will not block the flow of your program.
579 /// Once a sample starts playing the function call following
580 /// ts5_play_sample() will be executed immediately.
581 //@{
582 ////////////////////////////////////////////////////////////////////////////////
583 
584 
585 ////////////////////////////////////////////////////////////////////////////////
586 /// Start playing a sample.
587 ///
588 /// \param sample Pointer to the sample that will be played.
589 ////////////////////////////////////////////////////////////////////////////////
590 void ts5_play_sample(TS5_SAMPLE *sample)
591 {
592  ts5_check_audio("ts5_play_sample");
593  ts5_log(TS5_LOGLEVEL_5, "ts5_play_sample(%p)\n", sample);
594 
595  if (!sample) {
596  ts5_fatal("ts5_play_sample: sample pointer is null\n");
597  }
598 
599  if (sample->issample) {
600 
601  if (!al_set_sample_instance_playing(sample->instance, 1)) {
602  ts5_fatal("ts5_play_sample: could not play sample\n");
603  }
604  }
605  else if (sample->isfilestream) {
606 
607  if (!al_set_audio_stream_playing(sample->stream, 1)) {
608  ts5_fatal("ts5_play_sample: could not play sample\n");
609  }
610  }
611  else if (sample->ismemorystream) {
612  ts5_fatal("ts5_play_sample: memory streams not implemented yet\n");
613  }
614 }
615 
616 
617 ////////////////////////////////////////////////////////////////////////////////
618 /// Pause playing a sample.
619 ///
620 /// \param sample Pointer to the sample that will be paused.
621 ////////////////////////////////////////////////////////////////////////////////
622 void ts5_pause_sample(TS5_SAMPLE *sample)
623 {
624  ts5_check_audio("ts5_pause_sample");
625  ts5_log(TS5_LOGLEVEL_5, "ts5_pause_sample(%p)\n", sample);
626 
627  if (!sample) {
628  ts5_fatal("ts5_pause_sample: sample pointer is null\n");
629  }
630 
631  if (sample->issample) {
632 
633  unsigned int position =
634  al_get_sample_instance_position(sample->instance);
635 
636  if (!al_set_sample_instance_playing(sample->instance, 0)) {
637  ts5_fatal("ts5_pause_sample: could not pause sample\n");
638  }
639 
640  if (!al_set_sample_instance_position(sample->instance, position)) {
641  ts5_fatal("ts5_pause_sample: could not change sample position\n");
642  }
643  }
644  else if (sample->isfilestream) {
645 
646  if (!al_set_audio_stream_playing(sample->stream, 0)) {
647  ts5_fatal("ts5_pause_sample: could not pause sample\n");
648  }
649  }
650  else if (sample->ismemorystream) {
651  ts5_fatal("memory streams not implemented yet\n");
652  }
653 }
654 
655 
656 ////////////////////////////////////////////////////////////////////////////////
657 /// Stop playing a sample.
658 ///
659 /// \param sample Pointer to the sample that will be stopped.
660 ////////////////////////////////////////////////////////////////////////////////
661 void ts5_stop_sample(TS5_SAMPLE *sample)
662 {
663  ts5_check_audio("ts5_stop_sample");
664  ts5_log(TS5_LOGLEVEL_5, "ts5_stop_sample(%p)\n", sample);
665 
666  if (!sample) {
667  ts5_fatal("ts5_stop_sample: sample pointer is null\n");
668  }
669 
670  if (sample->issample) {
671 
672  if (!al_set_sample_instance_playing(sample->instance, 0)) {
673  ts5_fatal("ts5_stop_sample: could not stop sample\n");
674  }
675 
676  if (!al_set_sample_instance_position(sample->instance, 0)) {
677  ts5_fatal("ts5_stop_sample: could not stop sample\n");
678  }
679  }
680  else if (sample->isfilestream) {
681 
682  al_drain_audio_stream(sample->stream);
683 
684  if (!al_rewind_audio_stream(sample->stream)) {
685  ts5_fatal("ts5_stop_sample: could not rewind sample\n");
686  }
687  }
688  else if (sample->ismemorystream) {
689  ts5_fatal("ts5_stop_sample: memory streams not implemented yet\n");
690  }
691 }
692 
693 
694 ////////////////////////////////////////////////////////////////////////////////
695 //@}
696 ////////////////////////////////////////////////////////////////////////////////
697 
698 
699 ////////////////////////////////////////////////////////////////////////////////
700 /// @name Sample parameters
701 /// For each sample the parameters can be queried/set using the functions below:
702 ///
703 /// - Sample position, speed, gain, pan and playmode
704 /// can be adjusted at any time, even when the sample is playing.
705 /// - Sample frequency, channels, depth and length are defined when the
706 /// sample is read or allocated.
707 ///
708 //@{
709 ////////////////////////////////////////////////////////////////////////////////
710 
711 
712 ////////////////////////////////////////////////////////////////////////////////
713 /// Query whether a sample is playing.
714 ///
715 /// \param sample Pointer to the sample that will queried.
716 ///
717 /// \return 1 if the sample is playing, 0 if not.
718 ////////////////////////////////////////////////////////////////////////////////
719 int ts5_get_sample_status(TS5_SAMPLE *sample)
720 {
721  ts5_check_audio("ts5_get_sample_status");
722  ts5_log(TS5_LOGLEVEL_5, "ts5_get_sample_status(%p)\n", sample);
723 
724  if (!sample) {
725  ts5_fatal("ts5_get_sample_status: sample pointer is null\n");
726  }
727 
728  int retval = 0;
729 
730  if (sample->issample) {
731  retval = al_get_sample_instance_playing(sample->instance);
732  }
733  else if (sample->isfilestream) {
734  retval = al_get_audio_stream_playing(sample->stream);
735  }
736  else if (sample->ismemorystream) {
737  ts5_fatal("%s: %s\n", "ts5_get_sample_status",
738  "memory streams not implemented yet");
739  }
740  else {
741  ts5_fatal("ts5_get_sample_status: sample type unknown\n");
742  }
743 
744  return retval;
745 }
746 
747 
748 ////////////////////////////////////////////////////////////////////////////////
749 /// Get the sample rate of a sample.
750 ///
751 /// \param sample Pointer to the sample that will queried.
752 ///
753 /// \return The sample rate of the sample.
754 ////////////////////////////////////////////////////////////////////////////////
755 unsigned int ts5_get_sample_frequency(TS5_SAMPLE *sample)
756 {
757  ts5_check_audio("ts5_get_sample_frequency");
758  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_frequency(%p)\n", sample);
759 
760  if (!sample) {
761  ts5_fatal("ts5_get_sample_frequency: sample pointer is null\n");
762  }
763 
764  unsigned int retval = 0;
765 
766  if (sample->issample) {
767  retval = al_get_sample_frequency(sample->sample);
768  }
769  else if (sample->isfilestream) {
770  retval = al_get_audio_stream_frequency(sample->stream);
771  }
772  else if (sample->ismemorystream) {
773  ts5_fatal("%s: %s\n", "ts5_get_sample_frequency",
774  "memory streams not implemented yet");
775  }
776 
777  return retval;
778 }
779 
780 
781 ////////////////////////////////////////////////////////////////////////////////
782 /// Get the number of channels of a sample.
783 ///
784 /// \param sample Pointer to the sample that will queried.
785 ///
786 /// \return The number of channels of the sample.
787 ////////////////////////////////////////////////////////////////////////////////
788 unsigned int ts5_get_sample_channels(TS5_SAMPLE *sample)
789 {
790  ts5_check_audio("ts5_get_sample_channels");
791  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_channels(%p)\n", sample);
792 
793  if (!sample) {
794  ts5_fatal("ts5_get_sample_channels: sample pointer is null\n");
795  }
796 
797  ALLEGRO_CHANNEL_CONF channels;
798 
799  if (sample->issample) {
800  channels = al_get_sample_channels(sample->sample);
801  }
802  else if (sample->isfilestream) {
803  channels = al_get_audio_stream_channels(sample->stream);
804  }
805  else if (sample->ismemorystream) {
806  ts5_fatal("%s: %s\n", "ts5_get_sample_channels",
807  "memory streams not implemented yet");
808  }
809 
810  unsigned int retval = 0;
811 
812  if (channels == ALLEGRO_CHANNEL_CONF_1) {
813  retval = TS5_MONO;
814  }
815  else if (channels == ALLEGRO_CHANNEL_CONF_2) {
816  retval = TS5_STEREO;
817  }
818  else {
819  ts5_fatal("%s: %s\n", "ts5_get_sample_channels",
820  "got an unknown number of channels");
821  }
822 
823  return retval;
824 }
825 
826 
827 ////////////////////////////////////////////////////////////////////////////////
828 /// Get the sample depth of a sample.
829 ///
830 /// \param sample Pointer to the sample that will queried.
831 ///
832 /// \return The sample depth of a sample.
833 ////////////////////////////////////////////////////////////////////////////////
834 int ts5_get_sample_depth(TS5_SAMPLE *sample)
835 {
836  ts5_check_audio("ts5_get_sample_depth");
837  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_depth(%p)\n", sample);
838 
839  if (!sample) {
840  ts5_fatal("ts5_get_sample_depth: sample pointer is null\n");
841  }
842 
843  ALLEGRO_AUDIO_DEPTH depth;
844 
845  if (sample->issample) {
846  depth = al_get_sample_depth(sample->sample);
847  }
848  else if (sample->isfilestream) {
849  depth = al_get_audio_stream_depth(sample->stream);
850  }
851  else if (sample->ismemorystream) {
852  ts5_fatal("ts5_get_sample_depth: memory streams not implemented yet\n");
853  }
854 
855  int retval = 0;
856 
857  if (depth == ALLEGRO_AUDIO_DEPTH_INT16) {
858  retval = TS5_INTEGER;
859  }
860  else if (depth == ALLEGRO_AUDIO_DEPTH_FLOAT32) {
861  retval = TS5_FLOAT;
862  }
863  else {
864  ts5_fatal("ts5_get_audio_depth: got an unknown sample depth\n");
865  }
866 
867  return retval;
868 }
869 
870 
871 ////////////////////////////////////////////////////////////////////////////////
872 /// Get the length of a sample.
873 ///
874 /// \param sample Pointer to the sample that will queried.
875 ///
876 /// \return The length of the sample.
877 ////////////////////////////////////////////////////////////////////////////////
878 unsigned int ts5_get_sample_length(TS5_SAMPLE *sample)
879 {
880  ts5_check_audio("ts5_get_sample_length");
881  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_length(%p)\n", sample);
882 
883  if (!sample) {
884  ts5_fatal("ts5_get_sample_length: sample pointer is null\n");
885  }
886 
887  unsigned int retval = 0;
888 
889  if (sample->issample) {
890  retval = al_get_sample_length(sample->sample);
891  }
892  else if (sample->isfilestream) {
893  retval = al_get_audio_stream_length(sample->stream);
894  }
895  else if (sample->ismemorystream) {
896  ts5_fatal("%s: %s\n", "ts5_get_sample_length",
897  "memory streams not implemented yet");
898  }
899 
900  return retval;
901 }
902 
903 
904 
905 ////////////////////////////////////////////////////////////////////////////////
906 /// Set the playback position of a sample.
907 ///
908 /// \param sample Pointer to the sample that will be adjusted.
909 /// \param position The new playback position.
910 ///
911 /// \return The old playback position.
912 ////////////////////////////////////////////////////////////////////////////////
913 unsigned int ts5_set_sample_position(TS5_SAMPLE *sample, unsigned int position)
914 {
915  ts5_check_audio("ts5_set_sample_position");
916 
917  ts5_log(TS5_LOGLEVEL_4, "ts5_set_sample_position(%p,%u)\n",
918  sample, position);
919 
920  if (!sample) {
921  ts5_fatal("ts5_set_sample_position: sample pointer is null\n");
922  }
923 
924  unsigned int retval= 0;
925 
926  if (sample->issample) {
927 
928  unsigned int max = al_get_sample_length(sample->sample);
929 
930  if (position > max) {
931  ts5_fatal("ts5_set_sample_position: position should be",
932  " <= the length of the sample (%d)\n", max);
933  }
934 
935  retval = al_get_sample_instance_position(sample->instance);
936 
937  if (!(al_set_sample_instance_position(sample->instance, position))) {
938  ts5_fatal("%s: %s\n", "ts5_set_sample_position",
939  "could not change sample position");
940  }
941  }
942  else if (sample->isfilestream) {
943 
944  unsigned int freq = al_get_audio_stream_frequency(sample->stream);
945 
946  unsigned int max =
947  al_get_audio_stream_length_secs(sample->stream) * freq;
948 
949  if (position > max) {
950  ts5_fatal("ts5_set_sample_position: position should be",
951  " <= the length of the sample (%d)\n", max);
952  }
953 
954  retval = al_get_audio_stream_position_secs(sample->stream) * freq;
955 
956  if (!(al_seek_audio_stream_secs(sample->stream, position / freq))) {
957  ts5_fatal("%s: %s\n", "ts5_set_sample_position",
958  "could not change sample position");
959  }
960  }
961  else if (sample->ismemorystream) {
962  ts5_fatal("%s: %s\n", "ts5_set_sample_position",
963  "memory streams not implemented yet");
964  }
965 
966  return retval;
967 }
968 
969 
970 ////////////////////////////////////////////////////////////////////////////////
971 /// Get the playback position of a sample.
972 ///
973 /// \param sample Pointer to the sample that will be queried.
974 ///
975 /// \return The current playback position.
976 ////////////////////////////////////////////////////////////////////////////////
977 unsigned int ts5_get_sample_position(TS5_SAMPLE *sample)
978 {
979  ts5_check_audio("ts5_get_sample_position");
980  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_position(%p)\n", sample);
981 
982  if (!sample) {
983  ts5_fatal("ts5_get_sample_length: sample pointer is null\n");
984  }
985 
986  unsigned int retval = 0;
987 
988  if (sample->issample) {
989  retval = al_get_sample_instance_position(sample->instance);
990  }
991  else if (sample->isfilestream) {
992  unsigned int freq = al_get_audio_stream_frequency(sample->stream);
993  retval = al_get_audio_stream_position_secs(sample->stream) * freq;
994  }
995  else if (sample->ismemorystream) {
996  ts5_fatal("%s: %s\n", "ts5_get_sample_position",
997  "memory streams not implemented yet");
998  }
999 
1000  return retval;
1001 }
1002 
1003 
1004 ////////////////////////////////////////////////////////////////////////////////
1005 /// Set the playback speed of a sample.
1006 ///
1007 /// \param sample Pointer to the sample that will be adjusted.
1008 /// \param speed The new playback speed (1.0 is normal speed).
1009 ///
1010 /// \return The old playback speed.
1011 ////////////////////////////////////////////////////////////////////////////////
1012 double ts5_set_sample_speed(TS5_SAMPLE *sample, double speed)
1013 {
1014  ts5_check_audio("ts5_set_sample_speed");
1015  ts5_log(TS5_LOGLEVEL_4, "ts5_set_sample_speed(%p,%f)\n", sample, speed);
1016 
1017  if (!sample) {
1018  ts5_fatal("ts5_set_sample_speed: sample pointer is null\n");
1019  }
1020 
1021  if (speed < 0) {
1022  ts5_fatal("ts5_set_sample_speed: speed should be >0 (is %f)\n", speed);
1023  }
1024 
1025  double retval = 0.0;
1026 
1027  if (sample->issample) {
1028 
1029  retval = al_get_sample_instance_speed(sample->instance);
1030 
1031  if (!(al_set_sample_instance_speed(sample->instance, speed))) {
1032  ts5_fatal("%s: %s (%f)\n", "ts5_set_sample_speed",
1033  "could not change sample speed", speed);
1034  }
1035  }
1036  else if (sample->isfilestream) {
1037 
1038  retval = al_get_audio_stream_speed(sample->stream);
1039 
1040  if (!(al_set_audio_stream_speed(sample->stream, speed))) {
1041  ts5_fatal("%s: %s (%f)\n", "ts5_set_sample_speed",
1042  "could not change sample speed", speed);
1043  }
1044  }
1045  else if (sample->ismemorystream) {
1046  ts5_fatal("%s: %s\n", "ts5_set_sample_speed",
1047  "memory streams not implemented yet");
1048  }
1049 
1050  return retval;
1051 }
1052 
1053 
1054 ////////////////////////////////////////////////////////////////////////////////
1055 /// Get the playback speed of a sample.
1056 ///
1057 /// \param sample Pointer to the sample that will be queried.
1058 ///
1059 /// \return The current playback speed.
1060 ////////////////////////////////////////////////////////////////////////////////
1061 double ts5_get_sample_speed(TS5_SAMPLE *sample)
1062 {
1063  ts5_check_audio("ts5_get_sample_speed");
1064  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_speed(%p)\n", sample);
1065 
1066  if (!sample) {
1067  ts5_fatal("ts5_get_sample_speed: sample pointer is null\n");
1068  }
1069 
1070  double retval = 0.0;
1071 
1072  if (sample->issample) {
1073  retval = al_get_sample_instance_speed(sample->instance);
1074  }
1075  else if (sample->isfilestream) {
1076  retval = al_get_audio_stream_speed(sample->stream);
1077  }
1078  else if (sample->ismemorystream) {
1079  ts5_fatal("ts5_get_sample_speed: memory streams not implemented yet\n");
1080  }
1081 
1082  return retval;
1083 }
1084 
1085 
1086 ////////////////////////////////////////////////////////////////////////////////
1087 /// Set the playback gain of a sample.
1088 ///
1089 /// \param sample Pointer to the sample that will be adjusted.
1090 /// \param gain The new playback gain (1.0 is normal gain).
1091 ///
1092 /// \return The old gain position.
1093 ////////////////////////////////////////////////////////////////////////////////
1094 double ts5_set_sample_gain(TS5_SAMPLE *sample, double gain)
1095 {
1096  ts5_check_audio("ts5_set_sample_gain");
1097  ts5_log(TS5_LOGLEVEL_4, "ts5_set_sample_gain(%p,%f)\n", sample, gain);
1098 
1099  if (!sample) {
1100  ts5_fatal("ts5_set_sample_gain: sample pointer is null\n");
1101  }
1102 
1103  if (gain < 0) {
1104  ts5_fatal("ts5_set_sample_gain: gain should be >0 (is %f)\n", gain);
1105  }
1106 
1107  double retval = 0.0;
1108 
1109  if (sample->issample) {
1110 
1111  retval = al_get_sample_instance_gain(sample->instance);
1112 
1113  if (!(al_set_sample_instance_gain(sample->instance, gain))) {
1114  ts5_fatal("%s: %s (%f)\n", "ts5_set_sample_gain",
1115  "could not change sample gain", gain);
1116  }
1117  }
1118  else if (sample->isfilestream) {
1119 
1120  retval = al_get_audio_stream_gain(sample->stream);
1121 
1122  if (!(al_set_audio_stream_gain(sample->stream, gain))) {
1123  ts5_fatal("%s: %s (%f)\n", "ts5_set_sample_gain",
1124  "could not change sample gain", gain);
1125  }
1126  }
1127  else if (sample->ismemorystream) {
1128  ts5_fatal("ts5_set_sample_gain: memory streams not implemented yet\n");
1129  }
1130 
1131  return retval;
1132 }
1133 
1134 
1135 ////////////////////////////////////////////////////////////////////////////////
1136 /// Get the playback gain of a sample.
1137 ///
1138 /// \param sample Pointer to the sample that will be queried.
1139 ///
1140 /// \return The current playback gain.
1141 ////////////////////////////////////////////////////////////////////////////////
1142 double ts5_get_sample_gain(TS5_SAMPLE *sample)
1143 {
1144  ts5_check_audio("ts5_get_sample_gain");
1145  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_gain(%p)\n", sample);
1146 
1147  if (!sample) {
1148  ts5_fatal("ts5_get_sample_gain: sample pointer is null\n");
1149  }
1150 
1151  double retval = 0.0;
1152 
1153  if (sample->issample) {
1154  retval = al_get_sample_instance_gain(sample->instance);
1155  }
1156  else if (sample->isfilestream) {
1157  retval = al_get_audio_stream_gain(sample->stream);
1158  }
1159  else if (sample->ismemorystream) {
1160  ts5_fatal("ts5_get_sample_gain: memory streams not implemented yet\n");
1161  }
1162 
1163  return retval;
1164 }
1165 
1166 
1167 ////////////////////////////////////////////////////////////////////////////////
1168 /// Set the playback pan of a sample.
1169 ///
1170 /// \param sample Pointer to the sample that will be adjusted.
1171 /// \param pan The new playback pan
1172 /// (-1.0 is left, 0.0 is normal, 1.0 is right).
1173 ///
1174 /// \return The old playback pan.
1175 ////////////////////////////////////////////////////////////////////////////////
1176 double ts5_set_sample_pan(TS5_SAMPLE *sample, double pan)
1177 {
1178  ts5_check_audio("ts5_set_sample_pan");
1179  ts5_log(TS5_LOGLEVEL_4, "ts5_set_sample_pan(%p,%f)\n", sample, pan);
1180 
1181  if (!sample) {
1182  ts5_fatal("ts5_set_sample_pan: sample pointer is null\n");
1183  }
1184 
1185  if (pan < -1.0 || pan > 1) {
1186  ts5_fatal("%s: %s (is %f)\n", "ts5_set_sample_pan",
1187  "pan should be between -1.0 and 1.0", pan);
1188  }
1189 
1190  double retval = 0.0;
1191 
1192  if (sample->issample) {
1193 
1194  retval = al_get_sample_instance_pan(sample->instance);
1195 
1196  if (!(al_set_sample_instance_pan(sample->instance, pan))) {
1197  ts5_fatal("%s: %s (%f)\n", "ts5_set_sample_pan",
1198  "could not change sample pan", pan);
1199  }
1200  }
1201  else if (sample->isfilestream) {
1202 
1203  retval = al_get_audio_stream_pan(sample->stream);
1204 
1205  if (!(al_set_audio_stream_pan(sample->stream, pan))) {
1206  ts5_fatal("%s: %s (%f)\n", "ts5_set_sample_pan",
1207  "could not change sample pan", pan);
1208  }
1209  }
1210  else if (sample->ismemorystream) {
1211  ts5_fatal("ts5_set_sample_pan: memory streams not implemented yet\n");
1212  }
1213 
1214  return retval;
1215 }
1216 
1217 
1218 ////////////////////////////////////////////////////////////////////////////////
1219 /// Get the playback pan of a sample.
1220 ///
1221 /// \param sample Pointer to the sample that will be queried.
1222 ///
1223 /// \return The current playback pan.
1224 ////////////////////////////////////////////////////////////////////////////////
1225 double ts5_get_sample_pan(TS5_SAMPLE *sample)
1226 {
1227  ts5_check_audio("ts5_get_sample_pan");
1228  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_pan(%p)\n", sample);
1229 
1230  if (!sample) {
1231  ts5_fatal("ts5_get_sample_pan: sample pointer is null\n");
1232  }
1233 
1234  double retval = 0.0;
1235 
1236  if (sample->issample) {
1237  retval = al_get_sample_instance_pan(sample->instance);
1238  }
1239  else if (sample->isfilestream) {
1240  retval = al_get_audio_stream_pan(sample->stream);
1241  }
1242  else if (sample->ismemorystream) {
1243  ts5_fatal("memory streams not implemented yet\n");
1244  }
1245 
1246  return retval;
1247 }
1248 
1249 
1250 ////////////////////////////////////////////////////////////////////////////////
1251 /// Set the playmode of a sample.
1252 ///
1253 /// \param sample Pointer to the sample that will be adjusted.
1254 /// \param playmode The new playmode. Can be TS5_PLAY_ONCE or TS5_PLAY_LOOP.
1255 ///
1256 /// \return The old playmode.
1257 ////////////////////////////////////////////////////////////////////////////////
1258 int ts5_set_sample_playmode(TS5_SAMPLE *sample, int playmode)
1259 {
1260  ts5_check_audio("ts5_set_sample_playmode");
1261  ts5_log(TS5_LOGLEVEL_4, "ts5_set_sample_playmode(%p,%d)\n",
1262  sample, playmode);
1263 
1264  if (!sample) {
1265  ts5_fatal("ts5_set_sample_playmode: sample pointer is null\n");
1266  }
1267 
1268  if (playmode != TS5_PLAY_ONCE && playmode != TS5_PLAY_LOOP) {
1269  ts5_fatal("%s: %s\n", "ts5_set_sample_playmode",
1270  "playmode should be TS5_PLAY_ONCE or TS5_PLAY_LOOP");
1271  }
1272 
1273  int retval = 0;
1274 
1275  if (sample->issample) {
1276 
1277  retval = al_get_sample_instance_playmode(sample->instance);
1278 
1279  if (!(al_set_sample_instance_playmode(sample->instance, playmode))) {
1280  ts5_fatal("%s: %s\n", "ts5_set_sample_playmode",
1281  "could not change sample playmode");
1282  }
1283  }
1284  else if (sample->isfilestream) {
1285 
1286  retval = al_get_audio_stream_playmode(sample->stream) - 3;
1287 
1288  if (!(al_set_audio_stream_playmode(sample->stream, playmode))) {
1289  ts5_fatal("%s: %s\n", "ts5_set_sample_playmode",
1290  "could not change sample playmode");
1291  }
1292  }
1293  else if (sample->ismemorystream) {
1294  ts5_fatal("%s: %s\n", "ts5_set_sample_playmode",
1295  "memory streams not implemented yet");
1296  }
1297 
1298  return retval;
1299 }
1300 
1301 
1302 ////////////////////////////////////////////////////////////////////////////////
1303 /// Get the playmode of a sample.
1304 ///
1305 /// \param sample Pointer to the sample that will be queried.
1306 ///
1307 /// \return The current playmode.
1308 ////////////////////////////////////////////////////////////////////////////////
1309 int ts5_get_sample_playmode(TS5_SAMPLE *sample)
1310 {
1311  ts5_check_audio("ts5_get_sample_playmode");
1312  ts5_log(TS5_LOGLEVEL_4, "ts5_get_sample_playmode(%p)\n", sample);
1313 
1314  if (!sample) {
1315  ts5_fatal("ts5_get_sample_playmode: sample pointer is null\n");
1316  }
1317 
1318  int retval = 0;
1319 
1320  if (sample->issample) {
1321  retval = al_get_sample_instance_playmode(sample->instance);
1322  }
1323  else if (sample->isfilestream) {
1324  retval = al_get_audio_stream_playmode(sample->stream) - 3;
1325  }
1326  else if (sample->ismemorystream) {
1327  ts5_fatal("%s: %s\n", "ts5_get_sample_playmode",
1328  "memory streams not implemented yet");
1329  }
1330 
1331  return retval;
1332 }
1333 
1334 
1335 ////////////////////////////////////////////////////////////////////////////////
1336 //@}
1337 ////////////////////////////////////////////////////////////////////////////////
1338