/*
    GDAM - Geoff & Dave's Audio Mixer
    Copyright (C) 1999    Dave Benson, Geoff Matters.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

    Contact:
        daveb@ffem.org <Dave Benson>
        geoff@ugcs.caltech.edu <Geoff Matters>
*/
#ifndef __GDAM_SEQUENCER_H_
#define __GDAM_SEQUENCER_H_


typedef struct _GdamSequencer GdamSequencer;
typedef struct _GdamSequencerClass GdamSequencerClass;
typedef struct _GdamSequencerElement GdamSequencerElement;
typedef struct _GdamLocalSampleInfo GdamLocalSampleInfo;

#include "gdamproducer.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

GtkType gdam_sequencer_get_type();
#define GDAM_TYPE_SEQUENCER			(gdam_sequencer_get_type ())
#define GDAM_SEQUENCER(obj)              (GTK_CHECK_CAST ((obj), GDAM_TYPE_SEQUENCER, GdamSequencer))
#define GDAM_SEQUENCER_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GDAM_TYPE_SEQUENCER, GdamSequencerClass))
#define GDAM_IS_SEQUENCER(obj)           (GTK_CHECK_TYPE ((obj), GDAM_TYPE_SEQUENCER))
#define GDAM_IS_SEQUENCER_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GDAM_TYPE_SEQUENCER))


/* Contains all info about each instance that a sample is played within
 * a sequence.  The sample can be either a reference to a server-side
 * buffer (deprecated) or a reference to a local sample.  
 */
struct _GdamSequencerElement {
        /* Client-side id, for distinguishing different samples derived 
	 * from the same server-side buffer */
	int				local_id;

        /* The server-side id of the buffer to play. */
	guint				buffer_id;

	/* First sample in sequence to fill, in beat numbers. */
	gdouble                         beat_offset;

	/* Resample rate. */
	gdouble				rate;

	/* Gain. */
	gdouble				gain;

	/* The first sample in buffer_id to use. */
	int				sample_offset;

	/* The number of samples to output,
	 * or -1 for "as many as possible" */
	int				output_length;

	/* Used to apply temporary changes to sets of samples. */
	/* TODO: here, or in local info, or use only local info gain?. */
	gdouble				rate_factor;
	gdouble				gain_factor;
	gboolean			muted;

	/* Group elements into patterns. */
	int				pattern_number;
};


/* The LocalSample struct defines a sample - a chunk of buffer which can
 * be played many times within a sequence.  The LocalSample struct gives a 
 * way to track, describe, modify, and handle different samples which are
 * derived from the same server side buffer.
 */
struct _GdamLocalSampleInfo {
        /* Our id, how elements and others refer to us */
	int				local_id;

        /* The server-side id of the buffer to play */
	int				buffer_id;

	/* Resample rate, scale of 1.0 */
	gdouble				rate;

	/* Gain, scale of 1.0 */
	gdouble				gain;

	/* The first sample in buffer_id to use. */
	int				sample_offset;

	/* The number of samples to output,
	 * or -1 for "as many as possible" */
	int				output_length;

	/* When triggered to play freely, an additional rate
	 * factor can be applied.  */
	gdouble				trigger_rate;
	gdouble				trigger_gain;
};

struct _GdamSequencerClass {
	GdamProducerClass		producer_class;

	void                          (*sequence_changed)(GdamSequencer*);
	void                          (*set_num_beats)(GdamSequencer* seq,
	                                               gdouble       num_beats);
};

struct _GdamSequencer {
	GdamProducer		producer;
	GList		       *elements;

	/* Number of beats in the sequence. */
	gdouble 		num_beats;

	/* Number of subdivisions w/in each beat.  Used to determine
         * exact times for samples. (only used for guis)  */
	int			num_sub_beats;
	int			frozen;
	unsigned		making_request : 1;

	/* If this value is set, it is used to account for 
	 * lag when adding immediately. */
	gdouble			immediate_lag;

	/* Group elements into patterns, each pattern can be turned on/off.  */
	int			current_pattern;
	GArray                 *active_patterns;
	GArray                 *muted_patterns;

	/* Track samples by a local id, so that multiple samples can be
	 * derived from the same server-side buffer */
	GArray		       *local_infos;

	/* Keep track of our GdasSequence id (model->source_id is used for
	 * a GdasMix) */
	int			sequence_source_id;

	/* A list of source id's for freely triggered samples, matched by
	 * local buffer id (and possibly by pitch). */
	GSList                 *local_buffer_id_list;

	/* Used to remember the server-side source's current state */
	int			last_sequence_length_samples;

	/* How to rescale when bpm changes */
	gboolean		scale_element_lengths;

	int			default_playback_local_id;
	gboolean		do_record_local_play;

	/* until _reset_millis have passed w/o input, each note erases old 
	 * ones in the same time range */
	gboolean		auto_overwrite;
	gdouble			auto_overwrite_reset_millis;
	gboolean		auto_overplay;
	gdouble			last_local_play_beat_offset;
	GTimeVal		last_local_play_timeval;
};

GdamSequencer*          gdam_sequencer_new        ();

/* Allocate a new element and set default values */
GdamSequencerElement*   gdam_sequencer_new_element ();

/* Create a new element at a specific position within the sequence (beat_number)
 * and add it.
 */
GdamSequencerElement*	gdam_sequencer_add_element(GdamSequencer* sequencer,
	                                           int            buffer_id,
	                                           gdouble        beat_number,
	                                           gdouble        rate,
	                                           gdouble        gain,
	                                           int            sample_offset,
	                                           int            output_len);

/* Create a new element at the sequencer's current position and add it.
 */
GdamSequencerElement*	gdam_sequencer_add_element_immediate
                                                  (GdamSequencer* sequencer,
	                                           int            buffer_id,
	                                           gdouble        rate,
	                                           gdouble        gain,
	                                           int            sample_offset,
	                                           int            output_len);

void                    gdam_sequencer_remove_element
                                                  (GdamSequencer* sequencer,
						   GdamSequencerElement*);

/* Set the length of the sequence, as a number of beats. */
void                    gdam_sequencer_set_num_beats
                                                  (GdamSequencer* sequencer,
                                                   gdouble        num_beats);

/* Refresh the server's notion of the sequence. */
void                    gdam_sequencer_update_sequence
                                                  (GdamSequencer* sequencer);

void    	        gdam_sequencer_set_elements
                                                  (GdamSequencer* sequencer,
		                                   GList*         new_elements);
void                    gdam_sequencer_load       (GdamSequencer* sequencer,
                                                   const char*    filename);
void                    gdam_sequencer_save       (GdamSequencer* sequencer,
                                                   const char*    filename);

void                    gdam_sequencer_freeze     (GdamSequencer* sequencer);
void                    gdam_sequencer_thaw       (GdamSequencer* sequencer);
/* Sets the current pattern, i.e. the one being edited. */
void                    gdam_sequencer_set_pattern(GdamSequencer* sequencer,
						   int		  pattern_no);
void                    gdam_sequencer_pattern_on (GdamSequencer* sequencer,
						   int		  pattern_no);
void                    gdam_sequencer_pattern_off(GdamSequencer* sequencer,
						   int		  pattern_no);
gboolean                gdam_sequencer_pattern_get_active
						  (GdamSequencer* sequencer,
						   int		  pattern_no);
void gdam_sequencer_set_num_local_infos 	  (GdamSequencer* sequencer,
    					 	   int		  num);
int gdam_sequencer_add_local_info 		  (GdamSequencer *sequencer,
						   int            buffer_id,
						   int            sample_offset,
						   int            output_len);
/* Create a buffer containing the output of the played sequence */
void  			gdam_sequencer_cache      (GdamSequencer*  sequencer);

gdouble                 gdam_sequencer_get_magic_rate (GdamSequencer *sequencer,
                                                       int local_id);


#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
