|
@@ -0,0 +1,160 @@
|
|
|
+/*
|
|
|
+Copyright (C) 2015 by Bernd Buschinski <[email protected]>
|
|
|
+
|
|
|
+This program is free software: you can redistribute it and/or modify
|
|
|
+it under the terms of the GNU General Public License as published by
|
|
|
+the Free Software Foundation, either version 2 of the License, or
|
|
|
+(at your option) any later version.
|
|
|
+
|
|
|
+This program is distributed in the hope that it will be useful,
|
|
|
+but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+GNU General Public License for more details.
|
|
|
+
|
|
|
+You should have received a copy of the GNU General Public License
|
|
|
+along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+*/
|
|
|
+
|
|
|
+#include "jack-wrapper.h"
|
|
|
+
|
|
|
+#include <pthread.h>
|
|
|
+#include <stdio.h>
|
|
|
+
|
|
|
+#include <util/platform.h>
|
|
|
+
|
|
|
+#define blog(level, msg, ...) blog(level, "jack-input: " msg, ##__VA_ARGS__)
|
|
|
+
|
|
|
+/**
|
|
|
+ * Get obs speaker layout from number of channels
|
|
|
+ *
|
|
|
+ * @param channels number of channels reported by jack
|
|
|
+ *
|
|
|
+ * @return obs speaker_layout id
|
|
|
+ *
|
|
|
+ * @note This *might* not work for some rather unusual setups, but should work
|
|
|
+ * fine for the majority of cases.
|
|
|
+ */
|
|
|
+static enum speaker_layout jack_channels_to_obs_speakers(uint_fast32_t channels)
|
|
|
+{
|
|
|
+ switch(channels) {
|
|
|
+ case 1: return SPEAKERS_MONO;
|
|
|
+ case 2: return SPEAKERS_STEREO;
|
|
|
+ case 3: return SPEAKERS_2POINT1;
|
|
|
+ case 4: return SPEAKERS_SURROUND;
|
|
|
+ case 5: return SPEAKERS_4POINT1;
|
|
|
+ case 6: return SPEAKERS_5POINT1;
|
|
|
+ /* What should we do with 7 channels? */
|
|
|
+ /* case 7: return SPEAKERS_...; */
|
|
|
+ case 8: return SPEAKERS_7POINT1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return SPEAKERS_UNKNOWN;
|
|
|
+}
|
|
|
+
|
|
|
+int jack_process_callback(jack_nframes_t nframes, void* arg)
|
|
|
+{
|
|
|
+ struct jack_data* data = (struct jack_data*)arg;
|
|
|
+ if (data == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ pthread_mutex_lock(&data->jack_mutex);
|
|
|
+
|
|
|
+ struct obs_source_audio out;
|
|
|
+ out.speakers = jack_channels_to_obs_speakers(data->channels);
|
|
|
+ out.samples_per_sec = jack_get_sample_rate (data->jack_client);
|
|
|
+ /* format is always 32 bit float for jack */
|
|
|
+ out.format = AUDIO_FORMAT_FLOAT_PLANAR;
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < data->channels; ++i) {
|
|
|
+ jack_default_audio_sample_t *jack_buffer =
|
|
|
+ (jack_default_audio_sample_t *)jack_port_get_buffer(
|
|
|
+ data->jack_ports[i], nframes);
|
|
|
+ out.data[i] = (uint8_t *)jack_buffer;
|
|
|
+ }
|
|
|
+
|
|
|
+ out.frames = nframes;
|
|
|
+ out.timestamp = os_gettime_ns() -
|
|
|
+ jack_frames_to_time(data->jack_client, nframes);
|
|
|
+
|
|
|
+ obs_source_output_audio(data->source, &out);
|
|
|
+ pthread_mutex_unlock(&data->jack_mutex);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int_fast32_t jack_init(struct jack_data* data)
|
|
|
+{
|
|
|
+ pthread_mutex_lock(&data->jack_mutex);
|
|
|
+
|
|
|
+ if (data->jack_client != NULL)
|
|
|
+ goto good;
|
|
|
+
|
|
|
+ jack_options_t jack_option = data->start_jack_server ?
|
|
|
+ JackNullOption : JackNoStartServer;
|
|
|
+
|
|
|
+ data->jack_client = jack_client_open(data->device, jack_option, 0);
|
|
|
+ if (data->jack_client == NULL) {
|
|
|
+ blog(LOG_ERROR,
|
|
|
+ "jack_client_open Error:"
|
|
|
+ "Could not create JACK client! %s",
|
|
|
+ data->device);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ data->jack_ports = (jack_port_t**)bzalloc(
|
|
|
+ sizeof(jack_port_t*) * data->channels);
|
|
|
+ for (unsigned int i = 0; i < data->channels; ++i) {
|
|
|
+ char port_name[10] = {'\0'};
|
|
|
+ snprintf(port_name, sizeof(port_name), "in_%d", i+1);
|
|
|
+
|
|
|
+ data->jack_ports[i] = jack_port_register(data->jack_client,
|
|
|
+ port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
|
|
+ if (data->jack_ports[i] == NULL) {
|
|
|
+ blog(LOG_ERROR,
|
|
|
+ "jack_port_register Error:"
|
|
|
+ "Could not create JACK port! %s",
|
|
|
+ port_name);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (jack_set_process_callback(data->jack_client,
|
|
|
+ jack_process_callback, data) != 0) {
|
|
|
+ blog(LOG_ERROR, "jack_set_process_callback Error");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (jack_activate(data->jack_client) != 0) {
|
|
|
+ blog(LOG_ERROR,
|
|
|
+ "jack_activate Error:"
|
|
|
+ "Could not activate JACK client!");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+good:
|
|
|
+ pthread_mutex_unlock(&data->jack_mutex);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ pthread_mutex_unlock(&data->jack_mutex);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+void deactivate_jack(struct jack_data* data)
|
|
|
+{
|
|
|
+ pthread_mutex_lock(&data->jack_mutex);
|
|
|
+
|
|
|
+ if (data->jack_client) {
|
|
|
+ if (data->jack_ports != NULL) {
|
|
|
+ for (int i = 0; i < data->channels; ++i) {
|
|
|
+ if (data->jack_ports[i] != NULL)
|
|
|
+ jack_port_unregister(data->jack_client,
|
|
|
+ data->jack_ports[i]);
|
|
|
+ }
|
|
|
+ bfree(data->jack_ports);
|
|
|
+ data->jack_ports = NULL;
|
|
|
+ }
|
|
|
+ jack_client_close(data->jack_client);
|
|
|
+ data->jack_client = NULL;
|
|
|
+ }
|
|
|
+ pthread_mutex_unlock(&data->jack_mutex);
|
|
|
+}
|