//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: jack.cpp,v 1.1.1.1 2003/10/29 10:06:12 wschweer Exp $
//  (C) Copyright 2002 Werner Schweer (ws@seh.de)
//=========================================================

#include "config.h"
#if HAVE_JACK
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>

#include "audio.h"
#include "globals.h"

#include "jackaudio.h"

#ifndef RTCAP
extern void doSetuid();
extern void undoSetuid();
#endif

JackAudioDevice* jackAudioDevice = 0;

//---------------------------------------------------------
//   jackError
//---------------------------------------------------------

static void jackError(const char *s)
      {
      fprintf(stderr, "JACK ERROR: %s\n", s);
      }

//---------------------------------------------------------
//   noJackError
//---------------------------------------------------------

static void noJackError(const char *s)
      {
      }

//---------------------------------------------------------
//   initJackAudio
//    return true if JACK not found
//---------------------------------------------------------

bool initJackAudio()
      {
      if (debugMsg)
            fprintf(stderr, "init Jack Audio\n");
      jack_set_error_function(noJackError);

      doSetuid();

      jack_client_t* client = jack_client_new("MusE");

      if (client == 0) {
            fprintf(stderr, "no JACK audio server found\n");
            return true;
            }
      else {
            jack_set_error_function(jackError);

            jackAudioDevice = new JackAudioDevice(client);
            jackAudioDevice->registerClient();

            sampleRate  = jack_get_sample_rate(client);
            segmentSize = jack_get_buffer_size(client);
            }

      undoSetuid();
      return false;
      }
#if 0
static int bufsize_callback(jack_nframes_t n, void*)
      {
      printf("JACK: buffersize changed %ld\n", n);
      return 0;
      }
#endif
static int srate_callback(jack_nframes_t n, void*)
      {
      printf("JACK: sample rate changed: %ld\n", n);
      return 0;
      }
static void registration_callback(jack_port_id_t, int, void*)
      {
      printf("JACK: registration changed\n");
      }
static int graph_callback(void*)
      {
//      printf("JACK: graph changed\n");
      return 0;
      }
//static int xrun_callback(void*)
//      {
//      printf("JACK: xrun\n");
//      return 0;
//      }

//---------------------------------------------------------
//   register
//---------------------------------------------------------

void JackAudioDevice::registerClient()
      {
      jack_set_process_callback(client, processAudio, 0);
//      jack_set_buffer_size_callback(client, bufsize_callback, 0);
      jack_set_sample_rate_callback(client, srate_callback, 0);
      jack_set_port_registration_callback(client, registration_callback, 0);
      jack_set_graph_order_callback(client, graph_callback, 0);
//      jack_set_xrun_callback(client, xrun_callback, 0);

      out1 = jack_port_register(client, "output1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
      out2 = jack_port_register(client, "output2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
      if (out1 == 0 || out2 == 0)
            fprintf(stderr, "JACK: register ouput ports failed\n");
      in1  = jack_port_register(client, "input1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
      in2  = jack_port_register(client, "input2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
      if (in1 == 0 || in2 == 0)
            fprintf(stderr, "JACK: register input ports failed\n");
      }

//---------------------------------------------------------
//   ~JackAudioDevice
//---------------------------------------------------------

JackAudioDevice::~JackAudioDevice()
      {
      if (jack_client_close(client)) {
            fprintf(stderr, "jack_client_close() failed: %s\n",
               strerror(errno));
            }
      }

//---------------------------------------------------------
//   exitJackAudio
//---------------------------------------------------------

void exitJackAudio()
      {
      if (jackAudioDevice)
            delete jackAudioDevice;
      }

QString JackAudioDevice::open(int)
      {
      return QString("jack open");
      }

void JackAudioDevice::close()
      {
      }

//---------------------------------------------------------
//   start
//---------------------------------------------------------

void JackAudioDevice::start()
      {
      doSetuid();

//      printf("start JACK, uid effective: %d real: %d\n", geteuid(), getuid());
      if (jack_activate(client)) {
            fprintf (stderr, "JACK: cannot activate client\n");
            exit(-1);
            }
      /* connect the ports. Note: you can't do this before
         the client is activated, because we can't allow
         connections to be made to clients that aren't
         running.
       */

      _outputPort = "alsa_pcm";
      _inputPort = "alsa_pcm";
      if (jack_connect(client, jack_port_name(out1), "alsa_pcm:playback_1")) {
            fprintf (stderr, "JACK: cannot connect output port 1: %s\n",
               jack_port_name(out1));
            }
      if (jack_connect(client, jack_port_name(out2), "alsa_pcm:playback_2")) {
            fprintf (stderr, "JACK: cannot connect output port 2: %s\n",
               jack_port_name(out2));
            }
      if (jack_connect(client, "alsa_pcm:capture_1", jack_port_name(in1))) {
            fprintf (stderr, "JACK: cannot connect input port 1: %s\n",
               jack_port_name(in1));
            }
      if (jack_connect(client, "alsa_pcm:capture_2", jack_port_name(in2))) {
            fprintf (stderr, "JACK: cannot connect input port 2: %s\n",
               jack_port_name(in2));
            }
      undoSetuid();
      audio->setRunning(true);
//      int n = jack_port_get_latency(out1);  zero?
      }

//---------------------------------------------------------
//   stop
//---------------------------------------------------------

void JackAudioDevice::stop()
      {
      if (jack_deactivate(client)) {
            fprintf (stderr, "cannot activate client");
            }
      audio->setRunning(false);
      }

//---------------------------------------------------------
//   curPlayPos
//---------------------------------------------------------

int JackAudioDevice::curPlayPos() const
      {
      jack_nframes_t n = jack_frame_time(client);
      return (int)n;
      }

//---------------------------------------------------------
//   ports
//---------------------------------------------------------

std::list<QString> JackAudioDevice::outputPorts()
      {
// printf("Jack outputPorts\n");
      const char** ports = jack_get_ports(client, 0, 0, 0);
      std::list<QString> clientList;
      for (const char** p = ports; p && *p; ++p) {
            jack_port_t* port = jack_port_by_name(client, *p);
            int flags = jack_port_flags(port);
            if (!(flags & JackPortIsOutput))
                  continue;
            char buffer[128];
            strncpy(buffer, *p, 128);
            if (strncmp(buffer, "MusE", 4) == 0)
                  continue;
// printf("    <%s>\n", *p);
            clientList.push_back(QString(buffer));
            }
      return clientList;
      }

std::list<QString> JackAudioDevice::inputPorts()
      {
// printf("Jack inputPorts\n");
      const char** ports = jack_get_ports(client, 0, 0, 0);
      std::list<QString> clientList;
      for (const char** p = ports; p && *p; ++p) {
            jack_port_t* port = jack_port_by_name(client, *p);
            int flags = jack_port_flags(port);
            if (!(flags & JackPortIsInput))
                  continue;
            char buffer[128];
            strncpy(buffer, *p, 128);
            if (strncmp(buffer, "MusE", 4) == 0)
                  continue;
// printf("    <%s>\n", buffer);
            clientList.push_back(QString(buffer));
            }
      return clientList;
      }

void JackAudioDevice::setOutputPort(const QString& s)
      {
      _outputPort = s;
      printf("JackAudio: setOutputPort(%s\n", s.latin1());
      }

void JackAudioDevice::setInputPort(const QString& s)
      {
      _inputPort = s;
      printf("JackAudio: setInputPort(%s\n", s.latin1());
      }


//---------------------------------------------------------
//   getWriteBuffer
//---------------------------------------------------------

bool JackAudioDevice::getWriteBuffer(int n, unsigned long nframes, float** buffer)
      {
      buffer[0] = (float*) jack_port_get_buffer(out1, nframes);
      if (n == 2)
            buffer[1] = (float*) jack_port_get_buffer(out2, nframes);
      return true;
      }

//---------------------------------------------------------
//   read
//---------------------------------------------------------

void JackAudioDevice::read(int n, unsigned long nframes, float** buffer)
      {
      float* i1 = (float*) jack_port_get_buffer(in1, nframes);
      float* i2 = (float*) jack_port_get_buffer(in2, nframes);
      int nbytes = nframes * sizeof(float);
      if (n == 1) {
            memcpy(buffer[0], i1, nbytes);
            }
      else {
            memcpy(buffer[0], i1, nbytes);
            memcpy(buffer[1], i2, nbytes);
            }
      }

void JackAudioDevice::activate(bool)
      {
      }

#endif

