jack1 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
7.0KB

  1. /*
  2. * ALSA SEQ < - > JACK MIDI bridge
  3. *
  4. * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
  5. * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
  6. * Copyright (c) 2009,2010 Paul Davis <paul@linuxaudiosystems.com>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; version 2 of the License.
  11. *
  12. * This program 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
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. */
  21. #include <stdbool.h>
  22. #include <ctype.h>
  23. #include <semaphore.h>
  24. #include <alsa/asoundlib.h>
  25. #include <jack/jack.h>
  26. #include <jack/ringbuffer.h>
  27. #include "list.h"
  28. #include "a2j.h"
  29. #include "port_hash.h"
  30. #include "port.h"
  31. /* This should be part of JACK API */
  32. #define JACK_IS_VALID_PORT_NAME_CHAR(c) \
  33. (isalnum (c) || \
  34. (c) == '/' || \
  35. (c) == '_' || \
  36. (c) == '(' || \
  37. (c) == ')' || \
  38. (c) == '-' || \
  39. (c) == '[' || \
  40. (c) == ']')
  41. static
  42. int
  43. a2j_alsa_connect_from (alsa_midi_driver_t * driver, int client, int port)
  44. {
  45. snd_seq_port_subscribe_t* sub;
  46. snd_seq_addr_t seq_addr;
  47. int err;
  48. snd_seq_port_subscribe_alloca (&sub);
  49. seq_addr.client = client;
  50. seq_addr.port = port;
  51. snd_seq_port_subscribe_set_sender (sub, &seq_addr);
  52. seq_addr.client = driver->client_id;
  53. seq_addr.port = driver->port_id;
  54. snd_seq_port_subscribe_set_dest (sub, &seq_addr);
  55. snd_seq_port_subscribe_set_time_update (sub, 1);
  56. snd_seq_port_subscribe_set_queue (sub, driver->queue);
  57. snd_seq_port_subscribe_set_time_real (sub, 1);
  58. if ((err = snd_seq_subscribe_port (driver->seq, sub))) {
  59. a2j_error ("can't subscribe to %d:%d - %s", client, port, snd_strerror (err));
  60. }
  61. return err;
  62. }
  63. void
  64. a2j_port_setdead (a2j_port_hash_t hash, snd_seq_addr_t addr)
  65. {
  66. struct a2j_port *port = a2j_port_get (hash, addr);
  67. if (port) {
  68. port->is_dead = true; // see jack_process_internal
  69. } else {
  70. a2j_debug ("port_setdead: not found (%d:%d)", addr.client, addr.port);
  71. }
  72. }
  73. void
  74. a2j_port_free (struct a2j_port * port)
  75. {
  76. // snd_seq_disconnect_from (driver->seq, driver->port_id, port->remote.client, port->remote.port);
  77. // snd_seq_disconnect_to (driver->seq, driver->port_id, port->remote.client, port->remote.port);
  78. if (port->inbound_events) {
  79. jack_ringbuffer_free (port->inbound_events);
  80. }
  81. if (port->jack_port != JACK_INVALID_PORT && !port->driver_ptr->finishing) {
  82. jack_port_unregister (port->driver_ptr->jack_client, port->jack_port);
  83. }
  84. free (port);
  85. }
  86. void
  87. a2j_port_fill_name (struct a2j_port * port_ptr, int dir, snd_seq_client_info_t * client_info_ptr,
  88. const snd_seq_port_info_t * port_info_ptr, bool make_unique)
  89. {
  90. char *c;
  91. const char* const client_name = snd_seq_client_info_get_name (client_info_ptr);
  92. const char* const port_name = snd_seq_port_info_get_name (port_info_ptr);
  93. if (make_unique) {
  94. if (strstr (port_name, client_name) == port_name) {
  95. /* entire client name is part of the port name so don't replicate it */
  96. snprintf (port_ptr->name,
  97. sizeof(port_ptr->name),
  98. "[%d:%d] %s (%s)",
  99. snd_seq_client_info_get_client (client_info_ptr),
  100. snd_seq_port_info_get_port (port_info_ptr),
  101. port_name,
  102. (dir == A2J_PORT_CAPTURE ? "out" : "in"));
  103. } else {
  104. snprintf (port_ptr->name,
  105. sizeof(port_ptr->name),
  106. "[%d:%d] %s %s (%s)",
  107. snd_seq_client_info_get_client (client_info_ptr),
  108. snd_seq_port_info_get_port (port_info_ptr),
  109. client_name,
  110. port_name,
  111. (dir == A2J_PORT_CAPTURE ? "out" : "in"));
  112. }
  113. } else {
  114. if (strstr (port_name, client_name) == port_name) {
  115. /* entire client name is part of the port name so don't replicate it */
  116. snprintf (port_ptr->name,
  117. sizeof(port_ptr->name),
  118. "%s (%s)",
  119. port_name,
  120. (dir == A2J_PORT_CAPTURE ? "out" : "in"));
  121. } else {
  122. snprintf (port_ptr->name,
  123. sizeof(port_ptr->name),
  124. "%s %s (%s)",
  125. client_name,
  126. snd_seq_port_info_get_name (port_info_ptr),
  127. (dir == A2J_PORT_CAPTURE ? "out" : "in"));
  128. }
  129. }
  130. // replace all offending characters with ' '
  131. for (c = port_ptr->name; *c; ++c) {
  132. if (!JACK_IS_VALID_PORT_NAME_CHAR (*c)) {
  133. *c = ' ';
  134. }
  135. }
  136. }
  137. struct a2j_port *
  138. a2j_port_create (alsa_midi_driver_t * driver, int dir, snd_seq_addr_t addr, const snd_seq_port_info_t * info)
  139. {
  140. struct a2j_port *port;
  141. int err;
  142. int client;
  143. snd_seq_client_info_t * client_info_ptr;
  144. int jack_caps;
  145. struct a2j_stream * stream_ptr;
  146. stream_ptr = &driver->stream[dir];
  147. if ((err = snd_seq_client_info_malloc (&client_info_ptr)) != 0) {
  148. a2j_error ("Failed to allocate client info");
  149. goto fail;
  150. }
  151. client = snd_seq_port_info_get_client (info);
  152. err = snd_seq_get_any_client_info (driver->seq, client, client_info_ptr);
  153. if (err != 0) {
  154. a2j_error ("Failed to get client info");
  155. goto fail_free_client_info;
  156. }
  157. a2j_debug ("client name: '%s'", snd_seq_client_info_get_name (client_info_ptr));
  158. a2j_debug ("port name: '%s'", snd_seq_port_info_get_name (info));
  159. port = calloc (1, sizeof(struct a2j_port));
  160. if (!port) {
  161. goto fail_free_client_info;
  162. }
  163. port->driver_ptr = driver;
  164. port->jack_port = JACK_INVALID_PORT;
  165. port->remote = addr;
  166. a2j_port_fill_name (port, dir, client_info_ptr, info, false);
  167. /* Add port to list early, before registering to JACK, so map functionality is guaranteed to work during port registration */
  168. list_add_tail (&port->siblings, &stream_ptr->list);
  169. if (dir == A2J_PORT_CAPTURE) {
  170. jack_caps = JackPortIsOutput;
  171. } else {
  172. jack_caps = JackPortIsInput;
  173. }
  174. /* mark anything that looks like a hardware port as physical&terminal */
  175. if (snd_seq_port_info_get_type (info) & (SND_SEQ_PORT_TYPE_HARDWARE | SND_SEQ_PORT_TYPE_PORT | SND_SEQ_PORT_TYPE_SPECIFIC)) {
  176. jack_caps |= JackPortIsPhysical | JackPortIsTerminal;
  177. }
  178. port->jack_port = jack_port_register (driver->jack_client, port->name, JACK_DEFAULT_MIDI_TYPE, jack_caps, 0);
  179. if (port->jack_port == JACK_INVALID_PORT) {
  180. a2j_error ("jack_port_register() failed for '%s'", port->name);
  181. goto fail_free_port;
  182. }
  183. if (dir == A2J_PORT_CAPTURE) {
  184. err = a2j_alsa_connect_from (driver, port->remote.client, port->remote.port);
  185. } else {
  186. err = snd_seq_connect_to (driver->seq, driver->port_id, port->remote.client, port->remote.port);
  187. }
  188. if (err) {
  189. a2j_debug ("port skipped: %s", port->name);
  190. goto fail_free_port;
  191. }
  192. port->inbound_events = jack_ringbuffer_create (MAX_EVENT_SIZE * 16);
  193. a2j_debug ("port created: %s", port->name);
  194. return port;
  195. fail_free_port:
  196. list_del (&port->siblings);
  197. a2j_port_free (port);
  198. fail_free_client_info:
  199. snd_seq_client_info_free (client_info_ptr);
  200. fail:
  201. return NULL;
  202. }