Ejemplo n.º 1
0
static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
    SoundIoInStream *instream = &is->pub;
    SoundIoInStreamJack *isj = &is->backend_data.jack;
    SoundIoJack *sij = &si->backend_data.jack;
    SoundIoDevice *device = instream->device;
    SoundIoDevicePrivate *dev = (SoundIoDevicePrivate *)device;
    SoundIoDeviceJack *dj = &dev->backend_data.jack;

    if (sij->is_shutdown)
        return SoundIoErrorBackendDisconnected;

    if (!instream->name)
        instream->name = "SoundIoInStream";

    instream->software_latency = device->software_latency_current;
    isj->period_size = sij->period_size;

    jack_status_t status;
    isj->client = jack_client_open(instream->name, JackNoStartServer, &status);
    if (!isj->client) {
        instream_destroy_jack(si, is);
        assert(!(status & JackInvalidOption));
        if (status & JackShmFailure)
            return SoundIoErrorSystemResources;
        if (status & JackNoSuchClient)
            return SoundIoErrorNoSuchClient;
        return SoundIoErrorOpeningDevice;
    }

    int err;
    if ((err = jack_set_process_callback(isj->client, instream_process_callback, is))) {
        instream_destroy_jack(si, is);
        return SoundIoErrorOpeningDevice;
    }
    if ((err = jack_set_buffer_size_callback(isj->client, instream_buffer_size_callback, is))) {
        instream_destroy_jack(si, is);
        return SoundIoErrorOpeningDevice;
    }
    if ((err = jack_set_sample_rate_callback(isj->client, instream_sample_rate_callback, is))) {
        instream_destroy_jack(si, is);
        return SoundIoErrorOpeningDevice;
    }
    if ((err = jack_set_xrun_callback(isj->client, instream_xrun_callback, is))) {
        instream_destroy_jack(si, is);
        return SoundIoErrorOpeningDevice;
    }
    jack_on_shutdown(isj->client, instream_shutdown_callback, is);

    jack_nframes_t max_port_latency = 0;

    // register ports and map channels
    int connected_count = 0;
    for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
        SoundIoChannelId my_channel_id = instream->layout.channels[ch];
        const char *channel_name = soundio_get_channel_name(my_channel_id);
        unsigned long flags = JackPortIsInput;
        if (!instream->non_terminal_hint)
            flags |= JackPortIsTerminal;
        jack_port_t *jport = jack_port_register(isj->client, channel_name, JACK_DEFAULT_AUDIO_TYPE, flags, 0);
        if (!jport) {
            instream_destroy_jack(si, is);
            return SoundIoErrorOpeningDevice;
        }
        SoundIoInStreamJackPort *isjp = &isj->ports[ch];
        isjp->dest_port = jport;
        // figure out which source port this connects to
        SoundIoDeviceJackPort *djp = find_port_matching_channel(device, my_channel_id);
        if (djp) {
            isjp->source_port_name = djp->full_name;
            isjp->source_port_name_len = djp->full_name_len;
            connected_count += 1;
            max_port_latency = max(max_port_latency, djp->latency_range.max);
        }
    }
    // If nothing got connected, channel layouts aren't working. Just send the
    // data in the order of the ports.
    if (connected_count == 0) {
        max_port_latency = 0;
        instream->layout_error = SoundIoErrorIncompatibleDevice;

        int ch_count = min(instream->layout.channel_count, dj->port_count);
        for (int ch = 0; ch < ch_count; ch += 1) {
            SoundIoInStreamJackPort *isjp = &isj->ports[ch];
            SoundIoDeviceJackPort *djp = &dj->ports[ch];
            isjp->source_port_name = djp->full_name;
            isjp->source_port_name_len = djp->full_name_len;
            max_port_latency = max(max_port_latency, djp->latency_range.max);
        }
    }

    isj->hardware_latency = max_port_latency / (double)instream->sample_rate;

    return 0;
}
Ejemplo n.º 2
0
static int port_connected(struct GenesisNode *node) {
    struct ResampleContext *resample_context = (struct ResampleContext *)node->userdata;
    if (!resample_context->in_connected || !resample_context->out_connected)
        return 0;

    struct GenesisPort *audio_in_port = node->ports[0];
    struct GenesisPort *audio_out_port = node->ports[1];

    int in_sample_rate = genesis_audio_port_sample_rate(audio_in_port);
    int out_sample_rate = genesis_audio_port_sample_rate(audio_out_port);

    int gcd = greatest_common_denominator(in_sample_rate, out_sample_rate);
    resample_context->upsample_factor = out_sample_rate / gcd;
    resample_context->downsample_factor = in_sample_rate / gcd;

    resample_context->oversampled_rate = in_sample_rate * resample_context->upsample_factor;

    if (in_sample_rate == out_sample_rate) {
        destroy(resample_context->impulse_response, resample_context->impulse_response_size);
        resample_context->impulse_response = nullptr;
    } else {
        double cutoff_freq_hz = min(in_sample_rate, out_sample_rate) / 2.0;
        double cutoff_freq_float = cutoff_freq_hz / (double)resample_context->oversampled_rate;

        double transition_band = transition_band_hz / resample_context->oversampled_rate;
        int window_size = ceil(4.0 / transition_band);
        window_size += !(window_size % 2);

        float *new_impulse_response = reallocate_safe(resample_context->impulse_response,
                resample_context->impulse_response_size, window_size);

        if (!new_impulse_response)
            return GenesisErrorNoMem;

        resample_context->impulse_response = new_impulse_response;
        resample_context->impulse_response_size = window_size;

        // create impulse response by sampling the sinc function and then
        // multiplying by the blackman window
        for (int i = 0; i < window_size; i += 1) {
            // sample the sinc function
            double sinc_sample = sinc(2.0 * cutoff_freq_float * (i - (window_size - 1) / 2.0));
            double sample = sinc_sample * blackman_window(i, window_size);
            resample_context->impulse_response[i] = sample;
        }
    }

    // set up channel matrix
    const struct SoundIoChannelLayout * in_channel_layout = genesis_audio_port_channel_layout(audio_in_port);
    const struct SoundIoChannelLayout * out_channel_layout = genesis_audio_port_channel_layout(audio_out_port);
    memset(resample_context->channel_matrix, 0, sizeof(resample_context->channel_matrix));

    int in_contains[GENESIS_CHANNEL_ID_COUNT];
    int out_contains[GENESIS_CHANNEL_ID_COUNT];
    for (int id = 0; id < GENESIS_CHANNEL_ID_COUNT; id += 1) {
        in_contains[id] = soundio_channel_layout_find_channel(in_channel_layout, (SoundIoChannelId)id);
        out_contains[id] = soundio_channel_layout_find_channel(out_channel_layout, (SoundIoChannelId)id);
    }

    bool unaccounted[GENESIS_CHANNEL_ID_COUNT];
    for (int id = 0; id < GENESIS_CHANNEL_ID_COUNT; id += 1) {
        unaccounted[id] = in_contains[id] >= 0 && out_contains[id] == -1;
    }

    // route matching channel ids
    for (int id = 0; id < GENESIS_CHANNEL_ID_COUNT; id += 1) {
        if (in_contains[id] >= 0 && out_contains[id] >= 0)
            resample_context->channel_matrix[out_contains[id]][in_contains[id]] = 1.0;
    }

    // mix front center to left/right
    if (unaccounted[SoundIoChannelIdFrontCenter]) {
        if (out_contains[SoundIoChannelIdFrontLeft] >= 0 && out_contains[SoundIoChannelIdFrontRight] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontLeft]][in_contains[SoundIoChannelIdFrontCenter]] += M_SQRT1_2;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontRight]][in_contains[SoundIoChannelIdFrontCenter]] += M_SQRT1_2;
            unaccounted[SoundIoChannelIdFrontCenter] = false;
        }
    }

    // mix back left/right to back center, side or front
    if (unaccounted[SoundIoChannelIdBackLeft] && unaccounted[SoundIoChannelIdBackRight]) {
        if (out_contains[SoundIoChannelIdBackCenter] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdBackCenter]][in_contains[SoundIoChannelIdBackLeft]] += M_SQRT1_2;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdBackCenter]][in_contains[SoundIoChannelIdBackRight]] += M_SQRT1_2;
            unaccounted[SoundIoChannelIdBackLeft] = false;
            unaccounted[SoundIoChannelIdBackRight] = false;
        } else if (out_contains[SoundIoChannelIdSideLeft] >= 0 &&
                   out_contains[SoundIoChannelIdSideRight] >= 0)
        {
            if (in_contains[SoundIoChannelIdSideLeft] >= 0 &&
                in_contains[SoundIoChannelIdSideRight] >= 0)
            {
                resample_context->channel_matrix[out_contains[SoundIoChannelIdSideLeft]][in_contains[SoundIoChannelIdBackLeft]] += M_SQRT1_2;
                resample_context->channel_matrix[out_contains[SoundIoChannelIdSideRight]][in_contains[SoundIoChannelIdBackRight]] += M_SQRT1_2;
            } else {
                resample_context->channel_matrix[out_contains[SoundIoChannelIdSideLeft]][in_contains[SoundIoChannelIdBackLeft]] += 1.0;
                resample_context->channel_matrix[out_contains[SoundIoChannelIdSideRight]][in_contains[SoundIoChannelIdBackRight]] += 1.0;
            }
            unaccounted[SoundIoChannelIdBackLeft] = false;
            unaccounted[SoundIoChannelIdBackRight] = false;
        } else if (out_contains[SoundIoChannelIdFrontLeft] >= 0 &&
                   out_contains[SoundIoChannelIdFrontRight] >= 0)
        {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontLeft]][in_contains[SoundIoChannelIdBackLeft]] += surround_mix_level * M_SQRT1_2;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontRight]][in_contains[SoundIoChannelIdBackRight]] += surround_mix_level * M_SQRT1_2;
            unaccounted[SoundIoChannelIdBackLeft] = false;
            unaccounted[SoundIoChannelIdBackRight] = false;
        } else if (out_contains[SoundIoChannelIdFrontCenter] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdBackLeft]] += surround_mix_level * M_SQRT1_2;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdBackRight]] += surround_mix_level * M_SQRT1_2;
            unaccounted[SoundIoChannelIdBackLeft] = false;
            unaccounted[SoundIoChannelIdBackRight] = false;
        }
    }

    // mix side left/right into back or front
    if (unaccounted[SoundIoChannelIdSideLeft] && unaccounted[SoundIoChannelIdSideRight]) {
        if (out_contains[SoundIoChannelIdBackLeft] >= 0 &&
            out_contains[SoundIoChannelIdBackRight] >= 0)
        {
            // if back channels do not exist in the input, just copy side
            // channels to back channels, otherwise mix side into back
            if (in_contains[SoundIoChannelIdBackLeft] >= 0 &&
                in_contains[SoundIoChannelIdBackRight] >= 0)
            {
                resample_context->channel_matrix[out_contains[SoundIoChannelIdBackLeft]][in_contains[SoundIoChannelIdSideLeft]] += M_SQRT1_2;
                resample_context->channel_matrix[out_contains[SoundIoChannelIdBackRight]][in_contains[SoundIoChannelIdSideRight]] += M_SQRT1_2;
            } else {
                resample_context->channel_matrix[out_contains[SoundIoChannelIdBackLeft]][in_contains[SoundIoChannelIdSideLeft]] += 1.0;
                resample_context->channel_matrix[out_contains[SoundIoChannelIdBackRight]][in_contains[SoundIoChannelIdSideRight]] += 1.0;
            }
            unaccounted[SoundIoChannelIdSideLeft] = false;
            unaccounted[SoundIoChannelIdSideRight] = false;
        } else if (out_contains[SoundIoChannelIdBackCenter] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdBackCenter]][in_contains[SoundIoChannelIdSideLeft]] += M_SQRT1_2;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdBackCenter]][in_contains[SoundIoChannelIdSideRight]] += M_SQRT1_2;
            unaccounted[SoundIoChannelIdSideLeft] = false;
            unaccounted[SoundIoChannelIdSideRight] = false;
        } else if (out_contains[SoundIoChannelIdFrontLeft] >= 0 &&
                   out_contains[SoundIoChannelIdFrontRight] >= 0)
        {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontLeft]][in_contains[SoundIoChannelIdSideLeft]] += surround_mix_level;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontRight]][in_contains[SoundIoChannelIdSideRight]] += surround_mix_level;
            unaccounted[SoundIoChannelIdSideLeft] = false;
            unaccounted[SoundIoChannelIdSideRight] = false;
        } else if (out_contains[SoundIoChannelIdFrontCenter] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdSideLeft]] += surround_mix_level;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdSideRight]] += surround_mix_level;
            unaccounted[SoundIoChannelIdSideLeft] = false;
            unaccounted[SoundIoChannelIdSideRight] = false;
        }
    }

    // mix left of center/right of center into front left/right or center
    if (unaccounted[SoundIoChannelIdFrontLeftCenter] &&
        unaccounted[SoundIoChannelIdFrontRightCenter])
    {
        if (out_contains[SoundIoChannelIdFrontLeft] >= 0 && out_contains[SoundIoChannelIdFrontRight] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontLeft]][in_contains[SoundIoChannelIdFrontLeftCenter]] += 1.0;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontRight]][in_contains[SoundIoChannelIdFrontRightCenter]] += 1.0;
            unaccounted[SoundIoChannelIdFrontLeftCenter] = false;
            unaccounted[SoundIoChannelIdFrontRightCenter] = false;
        } else if (out_contains[SoundIoChannelIdFrontCenter] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdFrontLeftCenter]] += M_SQRT1_2;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdFrontRightCenter]] += M_SQRT1_2;
            unaccounted[SoundIoChannelIdFrontLeftCenter] = false;
            unaccounted[SoundIoChannelIdFrontRightCenter] = false;
        }
    }

    // mix LFE into front left/right or center
    if (unaccounted[SoundIoChannelIdLfe]) {
        if (out_contains[SoundIoChannelIdFrontCenter] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdLfe]] += lfe_mix_level;
            unaccounted[SoundIoChannelIdLfe] = false;
        } else if (out_contains[SoundIoChannelIdFrontLeft] >= 0 &&
                out_contains[SoundIoChannelIdFrontRight] >= 0)
        {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontLeft]][in_contains[SoundIoChannelIdLfe]] += lfe_mix_level * M_SQRT1_2;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontRight]][in_contains[SoundIoChannelIdLfe]] += lfe_mix_level * M_SQRT1_2;
            unaccounted[SoundIoChannelIdLfe] = false;
        }
    }

    // mix front left/right to front center
    if (unaccounted[SoundIoChannelIdFrontLeft] && unaccounted[SoundIoChannelIdFrontRight]) {
        if (out_contains[SoundIoChannelIdFrontCenter] >= 0) {
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdFrontLeft]] += 0.5;
            resample_context->channel_matrix[out_contains[SoundIoChannelIdFrontCenter]][in_contains[SoundIoChannelIdFrontRight]] += 0.5;
            unaccounted[SoundIoChannelIdFrontLeft] = false;
            unaccounted[SoundIoChannelIdFrontRight] = false;
        }
    }

    // make sure all input channels are accounted for
    for (int id = 0; id < GENESIS_CHANNEL_ID_COUNT; id += 1) {
        if (unaccounted[id]) {
            fprintf(stderr, "unaccounted: %s\n", soundio_get_channel_name((SoundIoChannelId)id));
            return GenesisErrorUnimplemented;
        }
    }

    // normalize per channel
    for (int out_ch = 0; out_ch < out_channel_layout->channel_count; out_ch += 1) {
        double sum = 0.0;
        for (int in_ch = 0; in_ch < in_channel_layout->channel_count; in_ch += 1) {
            sum += resample_context->channel_matrix[out_ch][in_ch];
        }
        if (sum > 0.0) {
            for (int in_ch = 0; in_ch < in_channel_layout->channel_count; in_ch += 1) {
                resample_context->channel_matrix[out_ch][in_ch] *= 1.0 / sum;
            }
        }
    }

    return 0;
}