/****************************************************************************
    
    ll-scope.c - The plugin part of the DSSI Oscilloscope plugin
    
    Copyright (C) 2005  Lars Luthman <larsl@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; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307  USA

****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>

#include <ladspa.h>
#include <dssi.h>

#include "dssi_shm.h"
#include "ringbuffer.h"
#include "ll-scope.h"


/* the plugin instance struct */
typedef struct {
  LADSPA_Data* input_buffer1;
  LADSPA_Data* input_buffer2;
  shared_data_t* shared_data;
  unsigned long sample_rate;
  int shm_id;
} DSSI_Scope;


/* data pointers */
LADSPA_Descriptor* ladspa_desc;
LADSPA_PortDescriptor* port_desc;
char** port_name;
LADSPA_PortRangeHint* port_hint;
DSSI_Descriptor* dssi_desc;


/** In this function we allocate a DSSI_Scope object and allocate shared
    memory to copy incoming data into. It should be called by the host. */
LADSPA_Handle instantiate(const LADSPA_Descriptor* descriptor,
			  unsigned long sample_rate) {
  DSSI_Scope* scope;
  /* fprintf(stderr, "Creating new DSSI Scope instance\n"); */
  scope = calloc(1, sizeof(DSSI_Scope));
  scope->sample_rate = sample_rate;
  return scope;
}


/** This function should be called by the host when something is connected
    to the input port. Nothing fancy here, just copy the pointer to the input
    buffer to our DSSI_Scope instance. */
void connect_port(LADSPA_Handle instance, unsigned long port,
		  LADSPA_Data* data_location) {
  DSSI_Scope* scope;
  /* fprintf(stderr, "Connecting input buffer to input port %u of DSSI Scope\n",
     port); */
  scope = instance;
  if (port == 0)
    scope->input_buffer1 = data_location;
  else if (port == 1)
    scope->input_buffer2 = data_location;
}


/** This function is run by the host when it wants to set a new configuration
    value (for example when the UI tells it to attach the shared memory
    segment). */
char* configure(LADSPA_Handle instance, const char *key, const char *value) {
  /* fprintf(stderr, "configure %s = %s\n", key, value); */
  DSSI_Scope* scope = instance;
  if (!strcmp(key, "shm_attach")) {
    shared_data_t* new_data = 
      (shared_data_t*)dssi_shm_attach(value, scope->shared_data);
    if (new_data) {
      scope->shared_data = new_data;
      scope->shared_data->sample_rate = scope->sample_rate;
      ringbuf_init(&(scope->shared_data->channel1.rb), 
		   sizeof(LADSPA_Data), BUFFER_SIZE);
      ringbuf_init(&(scope->shared_data->channel2.rb), 
		   sizeof(LADSPA_Data), BUFFER_SIZE);
    }
  }
  else if (!strcmp(key, "shm_detach")) {
    dssi_shm_detach(scope->shared_data);
    scope->shared_data = NULL;
  }
  return NULL;
}


/** This function is run by the host when it wants the plugin to process a
    chunk of audio data. We just copy it into our shared memory buffer. */
void run(LADSPA_Handle instance, unsigned long sample_count) {
  DSSI_Scope* scope = instance;
  if (scope->shared_data) {
    if (scope->input_buffer1)
      ringbuf_write(&(scope->shared_data->channel1.rb), scope->input_buffer1, 
		    sample_count);
    else
      ringbuf_write_zeros(&(scope->shared_data->channel1.rb), sample_count);
    if (scope->input_buffer2)
      ringbuf_write(&(scope->shared_data->channel2.rb), scope->input_buffer2, 
		    sample_count);
    else
      ringbuf_write_zeros(&(scope->shared_data->channel2.rb), sample_count);
  }
}


/** Just a wrapper function to make it work in jack-dssi-host 0.9 */
void run_synth(LADSPA_Handle instance, unsigned long sample_count,
	       snd_seq_event_t* Events, unsigned long EventCount) {
  run(instance, sample_count);
}


/** This is called by the host when it wants to dispose of the plugin instance.
    Here we free the shared memory buffer and the DSSI_Scope instance. */
void cleanup(LADSPA_Handle instance) {
  DSSI_Scope* scope;
  /* fprintf(stderr, "Cleaning up after a DSSI Scope instance\n"); */
  scope = instance;
  free(instance);
}


/** This is called automagically by the dynamic linker when the library
    is loaded. Initialise global stuff here. */
void _init() {
  
  /* allocate memory for LADSPA data */
  ladspa_desc = malloc(sizeof(LADSPA_Descriptor));
  memset(ladspa_desc, 0, sizeof(LADSPA_Descriptor));
  port_desc = calloc(2, sizeof(LADSPA_PortDescriptor));
  port_name = calloc(2, sizeof(char *));
  port_hint = calloc(2, sizeof(LADSPA_PortRangeHint));
  
  /* set LADSPA plugin info */
  ladspa_desc->UniqueID = 2745;
  ladspa_desc->Label = strdup("ll-scope");
  ladspa_desc->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
  ladspa_desc->Name = strdup("Oscilloscope");
  ladspa_desc->Maker = strdup("Lars Luthman");
  ladspa_desc->Copyright = strdup("GPL");
  ladspa_desc->PortCount = 2;
  
  /* set LADSPA port info */
  ladspa_desc->PortDescriptors = port_desc;
  port_desc[0] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
  port_desc[1] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
  ladspa_desc->PortNames = (const char**)port_name;
  port_name[0] = strdup("Channel 1");
  port_name[1] = strdup("Channel 2");
  ladspa_desc->PortRangeHints = port_hint;
  port_hint[0].HintDescriptor = 0;
  port_hint[1].HintDescriptor = 0;
  
  /* set LADSPA callbacks */
  ladspa_desc->instantiate = instantiate;
  ladspa_desc->connect_port = connect_port;
  ladspa_desc->activate = NULL;
  ladspa_desc->run = run;
  ladspa_desc->run_adding = NULL;
  ladspa_desc->set_run_adding_gain = NULL;
  ladspa_desc->deactivate = NULL;
  ladspa_desc->cleanup = cleanup;
  
  /* set DSSI info */
  dssi_desc = malloc(sizeof(DSSI_Descriptor));
  dssi_desc->DSSI_API_Version = 1;
  dssi_desc->LADSPA_Plugin = ladspa_desc;
  dssi_desc->configure = configure;
  dssi_desc->get_program = NULL;
  dssi_desc->get_midi_controller_for_port = NULL;
  dssi_desc->select_program = NULL;
  dssi_desc->run_synth = run_synth;
  dssi_desc->run_synth_adding = NULL;
  dssi_desc->run_multiple_synths = NULL;
  dssi_desc->run_multiple_synths_adding = NULL;
}


/** This is called automagically by the dynamic linker when the library is
    unloaded. Free global stuff here. */
void _fini() {
  int i;
  free(dssi_desc);
  free(ladspa_desc);
  free(port_desc);
  for (i = 0; i < 2; ++i)
    free(port_name[i]);
  free(port_name);
  free(port_hint);
}


/** The host expects this function to return a DSSI_Descriptor describing
    the plugin. */
const DSSI_Descriptor* dssi_descriptor(unsigned long index) {
  if (index == 0)
    return dssi_desc;
  return NULL;
}
