/*
#   audiofeed.c: jack connectivity for the streaming module of idjc
#   Copyright (C) 2007-2012 Stephen Fairchild (s-fairchild@users.sourceforge.net)
#
#   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 in the file entitled COPYING.
#   If not, see <http://www.gnu.org/licenses/>.
*/

#include "../config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include "sourceclient.h"
#include "main.h"
#include "audio_feed.h"

static struct audio_feed *audio_feed;

void audio_feed_supply_audio(sample_t *lc, sample_t *rc, jack_nframes_t n_frames)
    {
    struct audio_feed *self = audio_feed;
    struct threads_info *ti = self->threads_info;

    void process(struct audio_feed_data *afdata)
        {
        reevaluate:
        switch (afdata->jack_dataflow_control)
            {
            case JD_OFF:
                break;
            case JD_ON:
                while (jack_ringbuffer_write_space(afdata->input_rb[1]) < n_frames * sizeof (sample_t)) {
                    nanosleep(&(struct timespec){0, 100000}, NULL);
                    if (afdata->jack_dataflow_control != JD_ON)
                        goto reevaluate;
                }

                if (self->random_noise_f) {
                    sample_t noise;

                    for (int i = 0; i < n_frames; ++i)
                        for (int j = 0; j < 2; ++j) {
                            noise = (((sample_t)(rand() % 200)) - 100.0f) / 2000.0f;
                            jack_ringbuffer_write(afdata->input_rb[j], (char *)&noise, sizeof (sample_t));
                        }
                } else {
                    jack_ringbuffer_write(afdata->input_rb[0], (char *)lc, n_frames * sizeof (sample_t));
                    jack_ringbuffer_write(afdata->input_rb[1], (char *)rc, n_frames * sizeof (sample_t));
                }
                break;
            case JD_FLUSH:
                jack_ringbuffer_reset(afdata->input_rb[0]);
                jack_ringbuffer_reset(afdata->input_rb[1]);
                afdata->jack_dataflow_control = JD_OFF;
                break;
            default:
                fprintf(stderr, "jack_process_callback: unhandled jack_dataflow_control parameter\n");
            }
        }

    for (int i = 0; i < ti->n_encoders; i++)
        process(&(ti->encoder[i]->afdata));

    for (int i = 0; i < ti->n_recorders; i++)
        process(&(ti->recorder[i]->afdata));
    }

int audio_feed_jack_samplerate_request(struct threads_info *ti, struct universal_vars *uv, void *param)
    {
    fprintf(g.out, "idjcsc: sample_rate=%ld\n", (long)ti->audio_feed->sample_rate);
    fflush(g.out);
    if (ferror(g.out))
        return FAILED;
    return SUCCEEDED;
    }

struct audio_feed *audio_feed_init(struct threads_info *ti)
    {
    struct audio_feed *self;

    if (!(self = audio_feed = calloc(1, sizeof (struct audio_feed))))
        {
        fprintf(stderr, "audio_feed_init: malloc failure\n");
        return NULL;
        }

    self->threads_info = ti;
    self->sample_rate = jack_get_sample_rate(g.client);
    return self;
    }

void audio_feed_destroy(struct audio_feed *self)
    {
    self->threads_info->audio_feed = NULL;
    free(self);
    }

int audio_feed_random_noise_on(struct threads_info *ti, struct universal_vars *uv, void *param)
    {
    audio_feed->random_noise_f = 1;
    return SUCCEEDED;
    }

int audio_feed_random_noise_off(struct threads_info *ti, struct universal_vars *uv, void *param)
    {
    audio_feed->random_noise_f = 0;
    return SUCCEEDED;
    }
