jack-wrapper.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. Copyright (C) 2015 by Bernd Buschinski <[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 2 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 "jack-wrapper.h"
  15. #include <util/threading.h>
  16. #include <stdio.h>
  17. #include <util/platform.h>
  18. #define blog(level, msg, ...) blog(level, "jack-input: " msg, ##__VA_ARGS__)
  19. /**
  20. * Get obs speaker layout from number of channels
  21. *
  22. * @param channels number of channels reported by jack
  23. *
  24. * @return obs speaker_layout id
  25. *
  26. * @note This *might* not work for some rather unusual setups, but should work
  27. * fine for the majority of cases.
  28. */
  29. static enum speaker_layout jack_channels_to_obs_speakers(uint_fast32_t channels)
  30. {
  31. switch (channels) {
  32. case 1:
  33. return SPEAKERS_MONO;
  34. case 2:
  35. return SPEAKERS_STEREO;
  36. case 3:
  37. return SPEAKERS_2POINT1;
  38. case 4:
  39. return SPEAKERS_4POINT0;
  40. case 5:
  41. return SPEAKERS_4POINT1;
  42. case 6:
  43. return SPEAKERS_5POINT1;
  44. /* What should we do with 7 channels? */
  45. /* case 7: return SPEAKERS_...; */
  46. case 8:
  47. return SPEAKERS_7POINT1;
  48. }
  49. return SPEAKERS_UNKNOWN;
  50. }
  51. int jack_process_callback(jack_nframes_t nframes, void *arg)
  52. {
  53. struct jack_data *data = (struct jack_data *)arg;
  54. jack_nframes_t current_frames;
  55. jack_time_t current_usecs, next_usecs;
  56. float period_usecs;
  57. uint64_t now = os_gettime_ns();
  58. if (data == 0)
  59. return 0;
  60. struct obs_source_audio out;
  61. out.speakers = jack_channels_to_obs_speakers(data->channels);
  62. out.samples_per_sec = jack_get_sample_rate(data->jack_client);
  63. /* format is always 32 bit float for jack */
  64. out.format = AUDIO_FORMAT_FLOAT_PLANAR;
  65. for (unsigned int i = 0; i < data->channels; ++i) {
  66. jack_default_audio_sample_t *jack_buffer =
  67. (jack_default_audio_sample_t *)jack_port_get_buffer(
  68. data->jack_ports[i], nframes);
  69. out.data[i] = (uint8_t *)jack_buffer;
  70. }
  71. out.frames = nframes;
  72. if (!jack_get_cycle_times(data->jack_client, &current_frames,
  73. &current_usecs, &next_usecs, &period_usecs)) {
  74. out.timestamp = now - (int64_t)(period_usecs * 1000);
  75. } else {
  76. out.timestamp = now - util_mul_div64(nframes, 1000000000ULL,
  77. data->samples_per_sec);
  78. blog(LOG_WARNING,
  79. "jack_get_cycle_times error: guessing timestamp");
  80. }
  81. /* FIXME: this function is not realtime-safe, we should do something
  82. * about this */
  83. obs_source_output_audio(data->source, &out);
  84. return 0;
  85. }
  86. int_fast32_t jack_init(struct jack_data *data)
  87. {
  88. pthread_mutex_lock(&data->jack_mutex);
  89. if (data->jack_client != NULL)
  90. goto good;
  91. jack_options_t jack_option =
  92. data->start_jack_server ? JackNullOption : JackNoStartServer;
  93. data->jack_client = jack_client_open(data->device, jack_option, 0);
  94. if (data->jack_client == NULL) {
  95. blog(LOG_ERROR,
  96. "jack_client_open Error:"
  97. "Could not create JACK client! %s",
  98. data->device);
  99. goto error;
  100. }
  101. data->jack_ports =
  102. (jack_port_t **)bzalloc(sizeof(jack_port_t *) * data->channels);
  103. for (unsigned int i = 0; i < data->channels; ++i) {
  104. char port_name[10] = {'\0'};
  105. snprintf(port_name, sizeof(port_name), "in_%u", i + 1);
  106. data->jack_ports[i] = jack_port_register(
  107. data->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE,
  108. JackPortIsInput | JackPortIsTerminal, 0);
  109. if (data->jack_ports[i] == NULL) {
  110. blog(LOG_ERROR,
  111. "jack_port_register Error:"
  112. "Could not create JACK port! %s",
  113. port_name);
  114. goto error;
  115. }
  116. }
  117. if (jack_set_process_callback(data->jack_client, jack_process_callback,
  118. data) != 0) {
  119. blog(LOG_ERROR, "jack_set_process_callback Error");
  120. goto error;
  121. }
  122. if (jack_activate(data->jack_client) != 0) {
  123. blog(LOG_ERROR, "jack_activate Error:"
  124. "Could not activate JACK client!");
  125. goto error;
  126. }
  127. good:
  128. pthread_mutex_unlock(&data->jack_mutex);
  129. return 0;
  130. error:
  131. pthread_mutex_unlock(&data->jack_mutex);
  132. return 1;
  133. }
  134. void deactivate_jack(struct jack_data *data)
  135. {
  136. pthread_mutex_lock(&data->jack_mutex);
  137. if (data->jack_client) {
  138. jack_client_close(data->jack_client);
  139. if (data->jack_ports != NULL) {
  140. bfree(data->jack_ports);
  141. data->jack_ports = NULL;
  142. }
  143. data->jack_client = NULL;
  144. }
  145. pthread_mutex_unlock(&data->jack_mutex);
  146. }