audio-io.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "../util/threading.h"
  15. #include "../util/darray.h"
  16. #include "../util/circlebuf.h"
  17. #include "../util/platform.h"
  18. #include "audio-io.h"
  19. /* TODO: Incomplete */
  20. struct audio_line {
  21. struct audio_output *audio;
  22. struct circlebuf buffer;
  23. uint64_t base_timestamp;
  24. uint64_t last_timestamp;
  25. /* states whether this line is still being used. if not, then when the
  26. * buffer is depleted, it's destroyed */
  27. bool alive;
  28. };
  29. static inline void audio_line_destroy_data(struct audio_line *line)
  30. {
  31. circlebuf_free(&line->buffer);
  32. bfree(line);
  33. }
  34. struct audio_output {
  35. struct audio_info info;
  36. size_t block_size;
  37. media_t media;
  38. media_output_t output;
  39. pthread_t thread;
  40. event_t stop_event;
  41. DARRAY(uint8_t) pending_bytes;
  42. bool initialized;
  43. pthread_mutex_t line_mutex;
  44. DARRAY(struct audio_line*) lines;
  45. };
  46. static inline void audio_output_removeline(struct audio_output *audio,
  47. struct audio_line *line)
  48. {
  49. pthread_mutex_lock(&audio->line_mutex);
  50. da_erase_item(audio->lines, &line);
  51. pthread_mutex_unlock(&audio->line_mutex);
  52. audio_line_destroy_data(line);
  53. }
  54. /* ------------------------------------------------------------------------- */
  55. static void *audio_thread(void *param)
  56. {
  57. struct audio_output *audio = param;
  58. while (event_try(&audio->stop_event) == EAGAIN) {
  59. /* TODO */
  60. }
  61. return NULL;
  62. }
  63. /* ------------------------------------------------------------------------- */
  64. static inline bool valid_audio_params(struct audio_info *info)
  65. {
  66. return info->format && info->name && info->samples_per_sec > 0 &&
  67. info->speakers > 0;
  68. }
  69. static inline bool ao_add_to_media(audio_t audio)
  70. {
  71. struct media_output_info oi;
  72. oi.obj = audio;
  73. oi.connect = NULL;
  74. audio->output = media_output_create(&oi);
  75. if (!audio->output)
  76. return false;
  77. media_add_output(audio->media, audio->output);
  78. return true;
  79. }
  80. int audio_output_open(audio_t *audio, media_t media, struct audio_info *info)
  81. {
  82. struct audio_output *out;
  83. if (!valid_audio_params(info))
  84. return AUDIO_OUTPUT_INVALIDPARAM;
  85. out = bmalloc(sizeof(struct audio_output));
  86. memset(out, 0, sizeof(struct audio_output));
  87. memcpy(&out->info, info, sizeof(struct audio_info));
  88. pthread_mutex_init_value(&out->line_mutex);
  89. out->media = media;
  90. out->block_size = get_audio_channels(info->speakers) *
  91. get_audio_bytes_per_channel(info->format);
  92. if (pthread_mutex_init(&out->line_mutex, NULL) != 0)
  93. goto fail;
  94. if (event_init(&out->stop_event, true) != 0)
  95. goto fail;
  96. if (!ao_add_to_media(out))
  97. goto fail;
  98. if (pthread_create(&out->thread, NULL, audio_thread, out) != 0)
  99. goto fail;
  100. out->initialized = true;
  101. *audio = out;
  102. return AUDIO_OUTPUT_SUCCESS;
  103. fail:
  104. audio_output_close(out);
  105. return AUDIO_OUTPUT_FAIL;
  106. }
  107. audio_line_t audio_output_createline(audio_t audio)
  108. {
  109. struct audio_line *line = bmalloc(sizeof(struct audio_line));
  110. memset(line, 0, sizeof(struct audio_line));
  111. line->alive = true;
  112. pthread_mutex_lock(&audio->line_mutex);
  113. da_push_back(audio->lines, &line);
  114. pthread_mutex_unlock(&audio->line_mutex);
  115. return line;
  116. }
  117. const struct audio_info *audio_output_getinfo(audio_t audio)
  118. {
  119. return &audio->info;
  120. }
  121. void audio_output_close(audio_t audio)
  122. {
  123. void *thread_ret;
  124. size_t i;
  125. if (!audio)
  126. return;
  127. if (audio->initialized) {
  128. event_signal(&audio->stop_event);
  129. pthread_join(audio->thread, &thread_ret);
  130. }
  131. for (i = 0; i < audio->lines.num; i++)
  132. audio_line_destroy_data(audio->lines.array[i]);
  133. da_free(audio->lines);
  134. media_remove_output(audio->media, audio->output);
  135. event_destroy(&audio->stop_event);
  136. pthread_mutex_destroy(&audio->line_mutex);
  137. bfree(audio);
  138. }
  139. void audio_line_destroy(struct audio_line *line)
  140. {
  141. if (line) {
  142. if (!line->buffer.size)
  143. audio_output_removeline(line->audio, line);
  144. else
  145. line->alive = false;
  146. }
  147. }
  148. size_t audio_output_blocksize(audio_t audio)
  149. {
  150. return audio->block_size;
  151. }
  152. static inline uint64_t convert_to_sample_offset(audio_t audio, uint64_t offset)
  153. {
  154. return (uint64_t)((double)offset *
  155. (1000000000.0 / (double)audio->info.samples_per_sec));
  156. }
  157. void audio_line_output(audio_line_t line, const struct audio_data *data)
  158. {
  159. if (!line->buffer.size) {
  160. line->base_timestamp = data->timestamp;
  161. circlebuf_push_back(&line->buffer, data->data,
  162. data->frames * line->audio->block_size);
  163. } else {
  164. uint64_t position = data->timestamp - line->base_timestamp;
  165. position = convert_to_sample_offset(line->audio, position);
  166. position *= line->audio->block_size;
  167. circlebuf_place(&line->buffer, (size_t)position, data->data,
  168. data->frames * line->audio->block_size);
  169. }
  170. }