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

    sound.h

    Core sound interface functions and definitions.

****************************************************************************

    Copyright Aaron Giles
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in
          the documentation and/or other materials provided with the
          distribution.
        * Neither the name 'MAME' nor the names of its contributors may be
          used to endorse or promote products derived from this software
          without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.

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

#pragma once

#ifndef __EMU_H__
#error Dont include this file directly; include emu.h instead.
#endif

#ifndef __SOUND_H__
#define __SOUND_H__


//**************************************************************************
//  MACROS
//**************************************************************************

#define STREAM_UPDATE(name) void name(device_t *device, sound_stream *stream, void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples)



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

// forward references
class speaker_device;
typedef struct _wav_file wav_file;


// structure describing an indexed speaker
struct speaker_input
{
	speaker_device *	speaker;					// owning device
	sound_stream *		stream;						// stream within the device
	int					inputnum;					// input on the stream
};


// ======================> sound_stream

class sound_stream
{
	friend class simple_list<sound_stream>;
	friend class sound_manager;

	typedef void (*stream_update_func)(device_t *device, sound_stream *stream, void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples);

	// stream output class
	class stream_output
	{
	public:
		// construction/destruction
		stream_output();

		// internal state
		sound_stream *		m_stream;				// owning stream
		stream_sample_t *	m_buffer;				// output buffer
		int					m_dependents;			// number of dependents
		INT16				m_gain;					// gain to apply to the output
	};

	// stream input class
	class stream_input
	{
	public:
		// construction/destruction
		stream_input();

		// internal state
		stream_output *		m_source;				// pointer to the sound_output for this source
		stream_sample_t *	m_resample;				// buffer for resampling to the stream's sample rate
		UINT32				m_bufsize;				// size of output buffer, in samples
		UINT32				m_bufalloc;				// allocated size of output buffer, in samples
		attoseconds_t		m_latency_attoseconds;	// latency between this stream and the input stream
		INT16				m_gain;					// gain to apply to this input
		INT16				m_initial_gain;			// initial gain supplied at creation
	};

	// constants
	static const int OUTPUT_BUFFER_UPDATES		= 5;
	static const UINT32 FRAC_BITS				= 22;
	static const UINT32 FRAC_ONE				= 1 << FRAC_BITS;
	static const UINT32 FRAC_MASK				= FRAC_ONE - 1;

	// construction/destruction
	sound_stream(device_t &device, int inputs, int outputs, int sample_rate, void *param = NULL, stream_update_func callback = &sound_stream::device_stream_update_stub);

public:
	// getters
	sound_stream *next() const { return m_next; }
	device_t &device() const { return m_device; }
	int sample_rate() const { return (m_new_sample_rate != 0) ? m_new_sample_rate : m_sample_rate; }
	attotime sample_time() const;
	attotime sample_period() const { return attotime(0, m_attoseconds_per_sample); }
	int input_count() const { return m_inputs; }
	int output_count() const { return m_outputs; }
	float input_gain(int inputnum) const;
	float initial_input_gain(int inputnum) const;
	const char *input_name(int inputnum, astring &string) const;
	float output_gain(int outputnum) const;

	// operations
	void set_input(int inputnum, sound_stream *input_stream, int outputnum = 0, float gain = 1.0f);
	void update();
	const stream_sample_t *output_since_last_update(int outputnum, int &numsamples);

	// timing
	void set_sample_rate(int sample_rate);
	void set_input_gain(int inputnum, float gain);
	void set_output_gain(int outputnum, float gain);

private:
	// helpers called by our friends only
	void update_with_accounting(bool second_tick);
	void apply_sample_rate_changes();

	// internal helpers
	static STREAM_UPDATE( device_stream_update_stub );
	void recompute_sample_rate_data();
	void allocate_resample_buffers();
	void allocate_output_buffers();
	void postload();
	void generate_samples(int samples);
	stream_sample_t *generate_resampled_data(stream_input &input, UINT32 numsamples);

	// linking information
	device_t &			m_device;				// owning device
	sound_stream *		m_next;					// next stream in the chain

	// general information
	UINT32				m_sample_rate;			// sample rate of this stream
	UINT32				m_new_sample_rate;		// newly-set sample rate for the stream

	// timing information
	attoseconds_t		m_attoseconds_per_sample;// number of attoseconds per sample
	INT32				m_max_samples_per_update;// maximum samples per update

	// input information
	int					m_inputs;				// number of inputs
	stream_input *		m_input;				// list of streams we directly depend upon
	stream_sample_t **	m_input_array;			// array of inputs for passing to the callback

	// resample buffer information
	UINT32				m_resample_bufalloc;	// allocated size of each resample buffer

	// output information
	int					m_outputs;				// number of outputs
	stream_output *		m_output;				// list of streams which directly depend upon us
	stream_sample_t **	m_output_array;			// array of outputs for passing to the callback

	// output buffer information
	UINT32				m_output_bufalloc;		// allocated size of each output buffer
	INT32				m_output_sampindex;		// current position within each output buffer
	INT32				m_output_update_sampindex;// position at time of last global update
	INT32				m_output_base_sampindex;// sample at base of buffer, relative to the current emulated second

	// callback information
	stream_update_func	m_callback;				// callback function
	void *				m_param;				// callback function parameter
};


// ======================> sound_manager

class sound_manager
{
	friend class sound_stream;

	// reasons for muting
	static const UINT8 MUTE_REASON_PAUSE = 0x01;
	static const UINT8 MUTE_REASON_UI = 0x02;
	static const UINT8 MUTE_REASON_DEBUGGER = 0x04;
	static const UINT8 MUTE_REASON_SYSTEM = 0x08;

	// stream updates
	static const attotime STREAMS_UPDATE_ATTOTIME;

public:
	static const int STREAMS_UPDATE_FREQUENCY = 50;

	// construction/destruction
	sound_manager(running_machine &machine);
	~sound_manager();

	// getters
	running_machine &machine() const { return m_machine; }
	int attenuation() const { return m_attenuation; }
	sound_stream *first_stream() const { return m_stream_list.first(); }
	attotime last_update() const { return m_last_update; }
	attoseconds_t update_attoseconds() const { return m_update_attoseconds; }

	// stream creation
	sound_stream *stream_alloc(device_t &device, int inputs, int outputs, int sample_rate, void *param = NULL, sound_stream::stream_update_func callback = NULL);

	// global controls
	void set_attenuation(int attenuation);
	void ui_mute(bool turn_off = true) { mute(turn_off, MUTE_REASON_UI); }
	void debugger_mute(bool turn_off = true) { mute(turn_off, MUTE_REASON_DEBUGGER); }
	void system_mute(bool turn_off = true) { mute(turn_off, MUTE_REASON_SYSTEM); }
	void system_enable(bool turn_on = true) { mute(!turn_on, MUTE_REASON_SYSTEM); }

	// user gain controls
	bool indexed_speaker_input(int index, speaker_input &info) const;

private:
	// internal helpers
	void mute(bool mute, UINT8 reason);
	void reset();
	void pause();
	void resume();
	void config_load(int config_type, xml_data_node *parentnode);
	void config_save(int config_type, xml_data_node *parentnode);

	static TIMER_CALLBACK( update_static ) { reinterpret_cast<sound_manager *>(ptr)->update(); }
	void update();

	// internal state
	running_machine &	m_machine;				// reference to our machine
	emu_timer *			m_update_timer;			// timer to drive periodic updates

	UINT32				m_finalmix_leftover;
	INT16 *				m_finalmix;
	INT32 *				m_leftmix;
	INT32 *				m_rightmix;

	UINT8				m_muted;
	int 				m_attenuation;
	int 				m_nosound_mode;

	wav_file *			m_wavfile;

	// streams data
	simple_list<sound_stream> m_stream_list;	// list of streams
	attoseconds_t		m_update_attoseconds;	// attoseconds between global updates
	attotime			m_last_update;			// last update time
};


#endif	/* __SOUND_H__ */
