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

  Test Hardware Programming Interface (HPI) using HPI functions

  Usage: asihpitest --help

note to cleanup this file, use "astyle --style=linux -s4 asihpitest.c"

Copyright (C) 1997-2017 AudioScience, Inc. All rights reserved.

This software is provided 'as-is', without any express or implied warranty.
In no event will AudioScience Inc. be held liable for any damages arising
from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This copyright notice and list of conditions may not be altered or removed
   from any source distribution.

AudioScience, Inc. <support@audioscience.com>

( This license is GPL compatible see http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses )

******************************************************************************/
/* Some customization defines */
// Print out the complete list of mixer controls
#define DUMP_MIXER_CONTROLS 0
// Use background bus mastering if available
#define USE_BBM 1
// Record instreams to files "recN.pcm"
#define RECORD_TO_FILE 0
// Card polling interval in milliseconds
#define POLL_INTERVAL 500
// Test runtime
#define RUNTIME (15 * SECONDS)
// Interval at which stream state is printed
#define PRINT_INTERVAL (1 * SECONDS)

//#define GET_INFO_LOOP_TEST
/* End customization defines */

#define SECONDS (1000/POLL_INTERVAL)
#define MINUTES (60*SECONDS)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
//#include <unistd.h>
#include <getopt.h>
#define HPI_BUILD_INCLUDE_INTERNAL
#ifdef __APPLE__
#include <hpi/hpi.h>
#include <hpi/hpidebug.h>
#else
#include <hpi.h>
#include <hpi_version.h>
#include <hpidebug.h>
#endif

static char *hpi_control_strings[] = HPI_CONTROL_TYPE_STRINGS;
static char *hpi_src_strings[] = HPI_SOURCENODE_STRINGS;
static char *hpi_dst_strings[] = HPI_DESTNODE_STRINGS;

// local protos
static void HandleError(hpi_err_t err);

// file read/write
static void THPI_WavFileOpen(
	short nIndex,
	char *pszFile
);
static short THPI_WavFileRead(
	short nIndex,
	uint8_t *pbData,
	long lLength
);
static void THPI_WavFileClose(
	short nIndex
);
static int getch(
	void
);

// global
#define BLOCK_SIZE 32768L	//30720L //6144 //12288 //16384 //19200 //9600
static uint8_t abBuffer[BLOCK_SIZE];
static short haveFiles = 0;

/* Option variables */
static int instream_bbm = USE_BBM;
static int outstream_bbm = USE_BBM;
static unsigned int record_to_file = RECORD_TO_FILE;
static unsigned int runtime = RUNTIME + 1;
static unsigned int dump_mixer = 0;
static int max_instreams = 999;
static int max_outstreams = 999;
static uint16_t wAdapterIndex = 0;
static int nNumFiles = 0;
static unsigned int samplerate = 48000;
static unsigned int bitrate = 128000;
static unsigned int channels = 2;
static unsigned int format = HPI_FORMAT_PCM16_SIGNED;
static unsigned int new_mode = 0;
static unsigned int yes = 0;

#define MAX_FILES 8

static char szFile[MAX_FILES][80] = { "", "", "", "" };

static struct option long_options[] = {
	{"adapter", required_argument, 0, 'a'},
	{"dump-mixer", no_argument, 0, 'd'},
	{"disable-bbm", no_argument, 0, 'b'},
	{"yes", no_argument, 0, 'y'},
	{"adapter-mode", required_argument, 0, 'm'},
	{"samplerate", required_argument, 0, 's'},
	{"channels", required_argument, 0, 'c'},
	{"format", required_argument, 0, 'f'},
	{"file-record", no_argument, 0, 'F'},
	{"max-instreams", required_argument, 0, 'i'},
	{"max-outstreams", required_argument, 0, 'o'},
	{"runtime", required_argument, 0, 'r'},
	{"version", no_argument, 0, 'V'},
	{"help", no_argument, 0, 'h'},
	{0, 0, 0, 0}
};

static const char *short_options = "a:bc:df:Fhi:m:o:r:s:Vy?";

static const char *option_help[] = {
	"<adapter number> to test.",
	"Dump list of mixer controls.",
	"Disable use of background bus mastering.",
	"Don't prompt on error.",
	"Set the adapter mode. (need driver reload).",
	"<Hz> samplerate.",
	"<n> channels to play or record.",
	"<f> format index (see hpi.h)",
	"Record audio to disk files.",
	"<n> Max number of instreams to open.",
	"<n> Max number of outstreams to open.",
	"<runtime> in seconds.",
	"Show the version of this program",
	"Show this text."
};

#ifdef _MSC_VER
static void poll_delay(
	void
)
{
	Sleep(POLL_INTERVAL);
}
#else
#include <time.h>

#define millisec 1000000
static struct timespec poll_interval = {
	0, POLL_INTERVAL * millisec
};

static void poll_delay(
	void
)
{
	nanosleep(&poll_interval, 0);
}
#endif

static void help(
	void
)
{
	int i = 0;
	printf("\nUsage: asihpitest [options] [files to play]\n");
	printf("Exercise HPI API.\n\n");
	while (long_options[i].name != 0) {
		printf("  --%s -%c %s\n",
			long_options[i].name,
			(char)(long_options[i].val), option_help[i]);
		i++;
	}
	exit(0);
}

static void parse_options(
	int argc,
	char *argv[]
)
{
	int c;
	/*********** Parse the command line options ***************/
	while (1) {
		//int this_option_optind = optind ? optind : 1;
		int option_index = 0;

		c = getopt_long(argc, argv, short_options,
			long_options, &option_index);
		if (c == -1)
			break;

		switch (c) {
		case 0:
			printf("option %s", long_options[option_index].name);
			if (optarg)
				printf(" with arg %s", optarg);
			printf("\n");
			break;
		case 'a':
			wAdapterIndex = atoi(optarg);
			break;
		case 'b':
			instream_bbm = 0;
			outstream_bbm = 0;
			break;
		case 'c':
			channels = atoi(optarg);
			break;
		case 'd':
			dump_mixer = 1;
			break;
		case 'y':
			yes = 1;
			break;
		case 'F':
			record_to_file = 1;
			break;
		case 'f':
			format = atoi(optarg);
			break;
		case 'i':
			max_instreams = atoi(optarg);
			if (max_instreams < 0) {
				max_instreams = -max_instreams;
				instream_bbm = 0;
			}
			break;
		case 'm':
			new_mode = atoi(optarg);
			break;
		case 'o':
			max_outstreams = atoi(optarg);
			if (max_outstreams < 0) {
				max_outstreams = -max_outstreams;
				outstream_bbm = 0;
			}
			break;
		case 'r':
			runtime = atoi(optarg);
			runtime *= SECONDS;
			break;
		case 's':
			samplerate = atoi(optarg);
			break;
		case 'V':
			printf("asihpitest version " HPI_VER_STRING "\n");
			exit(0);
			break;
		case '?':
		case 'h':
			help();
			break;

		default:
			printf("?? getopt returned character code 0%o ??\n",
				c);
		}
	}

	if (optind < argc) {
		// printf ("non-option ARGV-elements: ");
		nNumFiles = 0;
		while ((optind < argc) && (nNumFiles < MAX_FILES)) {
			strcpy(szFile[nNumFiles], argv[optind++]);
			printf("File %d is %s\n", nNumFiles,
				szFile[nNumFiles]);
			nNumFiles++;
		}
	}

}


static void print_universal_control_lookup_result(
	hpi_hsubsys_t *hSubSys,
	hpi_handle_t hMixer,
	struct hpi_control_t asihpi_control,
	hpi_handle_t hControl
)
{
	static hpi_handle_t block_object;
	char name[256];
	hpi_err_t err;
	size_t size;
	size_t items;
	enum e_entity_role r;
	hpi_handle_t found_control;

	size = sizeof(name);
	err = HPI_Object_GetInfo( hControl,
		entity_type_cstring, entity_role_classname,
		name, &size, &items);

	if (err)
		HandleError(err);

	err = HPI_Object_GetRole(hControl, &r);
	if (err)
		HandleError(err);

	if ( r == entity_role_block ) {
		block_object = hControl;
		/* test lookup block by name */
		err = HPI_Object_BlockHandle(hMixer,
			asihpi_control.wSrcNodeType, asihpi_control.wSrcNodeIndex,
			asihpi_control.wDstNodeType, asihpi_control.wDstNodeIndex,
			name,
			&found_control );
	} else if ( r == entity_role_parameter_port ) {
		/* test lookup parameter by name */
		err = HPI_Object_ParameterHandle( hMixer, block_object, name, &found_control);
	}

	if (err) {
		HandleError(err);
		printf(" Block/Param lookup (FAILED) ");
	}
}

static void print_universal_control(
	hpi_hsubsys_t *hSubSys,
	hpi_handle_t hMixer,
	hpi_handle_t hControl
)
{
	struct hpi_entity *info;
	enum e_entity_role r;
	hpi_err_t err;
	char name[256];
	char units[256];
	size_t size, items;
	int flags;


	err = HPI_Object_GetInfoEntity(hControl, &info);

	printf("%s", HPI_Entity_GetRoleName(info));

	size = sizeof(name);
	err = HPI_Object_GetInfo( hControl,
			entity_type_cstring, entity_role_classname,
			name, &size, &items);
	if (err)
		HandleError(err);
	printf(" %s\n", name);

	err = HPI_Object_GetRole(hControl, &r);
	if (err)
		HandleError(err);

	if ( r == entity_role_parameter_port ) {
		struct hpi_entity *value;
		enum e_entity_type the_type;
		const char *type;
		void *v;
		printf("Value (");
		// flags
		size = sizeof(flags);
		err = HPI_Object_GetInfo( hControl,
			entity_type_int, entity_role_flags,
			&flags, &size, &items);
		if (flags & entity_flag_readable)
			printf("R");
		if (flags & entity_flag_writeable)
			printf("W");
		if (flags & entity_flag_volatile)
			printf("Volatile");
		printf(") ");

		err = HPI_Object_GetValueEntity(hControl, &value);
		err = HPI_Entity_Unpack(value, &the_type, &items, &r, &v);
		type = HPI_Entity_GetTypeName(value);
		if (!err) {
			int vi;
			printf("%s[%zd]=[ ", type, items);
			if (the_type == entity_type_mac_address) {
				the_type = entity_type_octet;
				items *= 6;
			}
			for (vi = 0; vi < items; vi++) {
				switch (the_type) {
				case entity_type_int:
					printf("%d ", ((int *)v)[vi]); break;
				case entity_type_float:
					printf("%f ", ((float *)v)[vi]); break;
				case entity_type_boolean:
					printf("%c ", ((char *)v)[vi]); break;
				case entity_type_cstring:
					printf("%c", ((char *)v)[vi]); break;
				case entity_type_ip4_address:
					printf("0x%08x ", ((int*)v)[vi]); break;
				case entity_type_octet:
				default:
					printf("%02x ", ((unsigned char *)v)[vi]); break;
			}

			}
			printf("] ");
		}
		HPI_Entity_Free(value);

		// units
		size = sizeof(units);
		err = HPI_Object_GetInfo( hControl,
			entity_type_cstring, entity_role_units,
			units, &size, &items);
		if (!err)
			printf("%s", units);
	}

	// fetch parameter handles
	if ( r == entity_role_block ) {
		hpi_handle_t params[16]; /* should really use count */
		size_t i;
		size_t number;

		number = sizeof(params)/sizeof(hpi_handle_t);
		err = HPI_Object_BlockParameters(
			hMixer,
			hControl,
			params,
			&number);
		if (err)
			HandleError(err);

		printf("Parameter handles=[");
		for ( i=0 ; i<number ; i++ ) {
			if (i > 0)
				printf(", ");
			printf("%d",params[i]);
		}
		printf("]");
	}
}

static void print_mixer_controls(
	hpi_hsubsys_t *hSubSys,
	hpi_handle_t hMixer
)
{
	int f;
	hpi_handle_t hControl;
	hpi_err_t err;
	for (f = 0; f < 10000; f++) {	// put SOME limit to the number
		struct hpi_control_t asihpi_control;

		err = HPI_MixerGetControlByIndex(hSubSys, hMixer, f, &asihpi_control.wSrcNodeType, &asihpi_control.wSrcNodeIndex, &asihpi_control.wDstNodeType, &asihpi_control.wDstNodeIndex, &asihpi_control.wControlType,	// HPI_CONTROL_METER, _VOLUME etc
			&hControl);
		if (err == HPI_ERROR_CONTROL_DISABLED)
			printf("DISABLED ");
		else if (err)
			break;

		printf("\nHPI Control %d, %s:%s[%d]->%s[%d] : ",
			f,
			hpi_control_strings[asihpi_control.wControlType],
			hpi_src_strings[asihpi_control.wSrcNodeType -
				HPI_SOURCENODE_NONE],
			asihpi_control.wSrcNodeIndex,
			hpi_dst_strings[asihpi_control.wDstNodeType -
				HPI_DESTNODE_NONE],
			asihpi_control.wDstNodeIndex);

		switch (asihpi_control.wControlType) {
			uint16_t s;
			uint32_t w;
			short db[2];

		case HPI_CONTROL_MULTIPLEXER:{
				int l;
				uint16_t cs, ci;

				err = HPI_Multiplexer_GetSource(hSubSys, hControl,
					&cs, &ci);
				if (err) {
					HandleError(err);
					break;
				}
				for (l = 0; l < 256; l++) {
					err = HPI_Multiplexer_QuerySource
						(hSubSys, hControl, l,
						&asihpi_control.wSrcNodeType,
						&asihpi_control.
						wSrcNodeIndex);
					if (!err) {
						if ((cs == asihpi_control.
								wSrcNodeType)
							&& (ci ==
								asihpi_control.
								wSrcNodeIndex))
							printf("\n->");
						else
							printf("\n");
						printf("\tSource %d %s[%d]",
							l,
							hpi_src_strings
							[asihpi_control.
								wSrcNodeType -
								HPI_SOURCENODE_NONE],
							asihpi_control.
							wSrcNodeIndex);
					} else
						break;
				}
				break;
			}
		case HPI_CONTROL_VOLUME:
			err = HPI_VolumeGetGain(hSubSys, hControl, db);
			if (err) {
				HandleError(err);
				break;
			}

			printf("%5.2fdB %5.2fdB", db[0] / 100.0,
				db[1] / 100.0);

			break;
            case HPI_CONTROL_LEVEL:
                err = HPI_LevelGetGain(hSubSys, hControl, db);
                if (err) {
                    HandleError(err);
                    break;
                }

                printf("%5.2fdB %5.2fdB", db[0] / 100.0,
                       db[1] / 100.0);

                break;
            case HPI_CONTROL_METER:
			err = HPI_MeterGetPeak(hSubSys, hControl, db);
			if (err) {
				HandleError(err);
				break;
			}

			printf("%5.2fdB %5.2fdB", db[0] / 100.0,
				db[1] / 100.0);
			break;
		case HPI_CONTROL_AESEBU_RECEIVER:
			err = HPI_AESEBU_Receiver_GetErrorStatus(hSubSys,
				hControl, &s);
			printf("status %02x", s);

			err = HPI_AESEBU_Receiver_GetFormat(hSubSys, hControl,
				&s);
			printf(" format %d", s);

			err = HPI_AESEBU_Receiver_GetSampleRate(hSubSys,
				hControl, &w);
			if (!err)
				printf(" rate %d", w);
			break;

		case HPI_CONTROL_SAMPLECLOCK:
			err = HPI_SampleClock_GetSampleRate(hSubSys, hControl,
				&w);
			err = HPI_SampleClock_GetSource(hSubSys, hControl,
				&s);
			printf("rate %d, source %d", w, s);
			break;
		case HPI_CONTROL_UNIVERSAL:
			print_universal_control_lookup_result(hSubSys, hMixer,
				asihpi_control,	hControl);
			print_universal_control(hSubSys, hMixer, hControl);
			break;
		}
	}

	printf("\n%d controls found\n", f);
}

/************************************** MAIN ***********************/
int main(
	int argc,
	char *argv[]
)
{
	hpi_hsubsys_t *hSubSys;
	hpi_err_t err = 0;		// HPI error
	uint32_t dwVersion = 0;
	uint32_t dwDataSizeW, dwDataSizeR = 0;
	struct hpi_format FormatW, FormatR;
	int numAdapters = 0;
	int adapterFound = 0;
	uint16_t wVersion;
	uint32_t dwSerialNumber;
	uint16_t wType;
	uint16_t wNumOutStreams;
	uint16_t wNumInStreams;
	uint32_t current_mode;

	hpi_handle_t hOutStream[16];
	hpi_handle_t hInStream[16];

	uint32_t dwStrBufferSize = 0;
	uint32_t dwIStrBufferSize = 0;

	hpi_handle_t hMixer = 0;
	hpi_handle_t hControl;
	hpi_handle_t hMeterControl = 0;
	hpi_handle_t ahGainControl[4] = { 0, 0, 0, 0 };

	short f;

	uint32_t i = 0;
	short anGainLog[4][2];

	long testcount = 0;

	//////////////////////////////////////////////////////////////////////////
	// #include "msgsize.c"

	parse_options(argc, argv);

	printf("********************************************************************\n");
	printf("\n** Test HPI using Functions **\n");

	if (nNumFiles == 0) {
		haveFiles = 0;
		nNumFiles = 32;
	} else
		haveFiles = 1;

	/////////////////////////////////////////////////////////////////////////////
	// open subsystem and find adapters
	err = HPI_SubSysSetHostNetworkInterface(NULL, "");
	if (err && (err != HPI_ERROR_INVALID_FUNC) ) {
		printf("HPI_SubSysSetHostNetworkInterface error\n");
		HandleError(err);
	}

	printf("********************************************************************\n");
	printf("HPI_SubSysCreate\n");
	hSubSys = HPI_SubSysCreate();
	if (hSubSys == NULL) {
		printf("hSubSys==NULL\n");
		exit(1);
	}

	err = HPI_SubSysGetVersionEx(hSubSys, &dwVersion);
	HandleError(err);
	printf("HPI_SubSysGetVersionEx=%x\n", dwVersion);

	err = HPI_SubSysGetNumAdapters(hSubSys, &numAdapters);
	printf("%li HPI_SubSysFindAdapters found %d adapters\n ", testcount++,
		numAdapters);
	HandleError(err);

	if (numAdapters > HPI_MAX_ADAPTERS)
		numAdapters = HPI_MAX_ADAPTERS;

	for (i = 0; i < (uint32_t)numAdapters; i++) {
		uint32_t dwAdapterIndex;
		uint16_t wAdapterType;
		err =  HPI_SubSysGetAdapter(hSubSys,
		i,
		&dwAdapterIndex,	///< Index of adapter.
		&wAdapterType	///< Type of adapter.
		)	;
		printf("%d=%X\n ", dwAdapterIndex, wAdapterType);
		if (dwAdapterIndex == wAdapterIndex)
			adapterFound = 1;
	}

	if (!adapterFound) {
		printf("No adapter with index %d\n", wAdapterIndex);
		exit(1);
	}

	err = HPI_AdapterClose(hSubSys, wAdapterIndex);
	if ( err && (err != HPI_ERROR_OBJ_NOT_OPEN) )
		HandleError(err);
	printf("HPI_AdapterClose \n");

	////////////////////////////////////////////////////////////////////////////
	// open 1st adapter
	err = HPI_AdapterOpen(hSubSys, wAdapterIndex);
	HandleError(err);
	printf("HPI_AdapterOpen \n");

#ifdef GET_INFO_LOOP_TEST
	while(1){
		poll_delay();
		err = HPI_AdapterGetInfo(hSubSys,
			wAdapterIndex,
			&wNumOutStreams,
			&wNumInStreams, &wVersion, &dwSerialNumber, &wType);
		HandleError(err);
		printf("HPI_AdapterGetInfo\n");
		printf("Adapter ID=%4X Index=%d NumOutStreams=%d NumInStreams=%d S/N=%d\nHw Version %c%d DSP code version %03d\n", wType, wAdapterIndex, wNumOutStreams, wNumInStreams, dwSerialNumber, ((wVersion >> 3) & 0xf) + 'A',	// Hw version major
			wVersion & 0x7,	// Hw version minor
			((wVersion >> 13) * 100) + ((wVersion >> 7) & 0x3f)	// DSP code version
			);
	}
	HPI_SubSysFree(hSubSys);

	return 0;
#else
	err = HPI_AdapterGetInfo(hSubSys,
		wAdapterIndex,
		&wNumOutStreams,
		&wNumInStreams, &wVersion, &dwSerialNumber, &wType);
	HandleError(err);
	printf("HPI_AdapterGetInfo\n");
	printf("Adapter ID=%4X Index=%d NumOutStreams=%d NumInStreams=%d S/N=%d\nHw Version %c%d DSP code version %03d\n", wType, wAdapterIndex, wNumOutStreams, wNumInStreams, dwSerialNumber, ((wVersion >> 3) & 0xf) + 'A',	// Hw version major
		wVersion & 0x7,	// Hw version minor
		((wVersion >> 13) * 100) + ((wVersion >> 7) & 0x3f)	// DSP code version
		);

	// apply the max streams options
	if (wNumOutStreams > max_outstreams)
		wNumOutStreams = max_outstreams;
	if (wNumInStreams > max_instreams)
		wNumInStreams = max_instreams;

	if (nNumFiles > wNumOutStreams) {
		nNumFiles = wNumOutStreams;
	}

	err = HPI_AdapterGetMode(hSubSys, wAdapterIndex, &current_mode);

	printf("Adapter current mode %d\n", current_mode);
	if (new_mode && (new_mode != current_mode)) {
		err = HPI_AdapterSetMode(hSubSys, wAdapterIndex, new_mode);
		HandleError(err);

		printf("Changed adapter mode. Reload driver to activate.\n");
		exit(0);
	}
	////////////////////////////////////////////////////////////////////////////
	// open the mixer of this adapter
	err = HPI_MixerOpen(hSubSys, wAdapterIndex, &hMixer);
	printf("HPI_MixerOpen: handle=%08X\n", hMixer);
	HandleError(err);

	if (dump_mixer)
		print_mixer_controls(hSubSys, hMixer);

	err = HPI_MixerGetControl(hSubSys, hMixer,
		HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK,
		&hControl);

	if (!err) {
		err = HPI_SampleClock_SetLocalRate(hSubSys, hControl, samplerate);
		/* not all adapters support the above call ie ASI2416s */
		if (err)
			printf("HPI_SampleClock_SetLocalRate() error %d\n",err);
	}
	////////////////////////////////////////////////////////////////////////////
#ifndef HPI_BUILD_NO_STREAMS
	if (wNumOutStreams) {
		// open the out_streams
		printf("HPI_OutStreamOpen\n");
		for (f = 0; f < wNumOutStreams; f++) {
			err = HPI_OutStreamOpen(hSubSys,
				wAdapterIndex, f, &hOutStream[f]
				);
			printf(" %i:%08X ", f, hOutStream[f]);
			HandleError(err);

			if (outstream_bbm) {
				printf("HPI_OutStreamHostBufferAllocate ");
				err = HPI_OutStreamHostBufferAllocate(hSubSys,
					hOutStream[f], BLOCK_SIZE);
				if (err == HPI_ERROR_INVALID_FUNC)
					outstream_bbm = 0;
				else
					HandleError(err);
			}

			err = HPI_OutStreamReset(hSubSys, hOutStream[f]);
			HandleError(err);
			// find out some info about the stream
			if (hOutStream[f]) {
				err = HPI_OutStreamGetInfoEx(hSubSys, hOutStream[f], NULL,	// state
					&dwStrBufferSize, NULL, NULL, NULL);
				HandleError(err);
				printf(" HPI_OutStreamGetInfo %d: BufferSize=%d\n", f, dwStrBufferSize);
			}
		}
	}
	////////////////////////////////////////////////////////////////////////////
	// setup format and size of data block
	// we will use to send audio data to out_stream
	// we would normally get the format directly from the file
	// or audio data
	err = HPI_FormatCreate(
		&FormatW,
		channels,
		format,
		samplerate,
		bitrate,
		0		// no attributes
		);
	HandleError(err);

	dwDataSizeW = BLOCK_SIZE / 2;
	////////////////////////////////////////////////////////////////////////////
	if (wNumInStreams) {
		// if the adapter has instreams then open them
		printf("\nHPI_InStreamOpen ");
		for (f = 0; f < wNumInStreams; f++) {

			err = HPI_InStreamOpen(hSubSys,
				wAdapterIndex, f, &hInStream[f]
				);
			printf(" %i:%08X ", f, hInStream[f]);
			HandleError(err);
		}

		if (wNumInStreams) {
			// find out some info about stream 0
			err = HPI_InStreamGetInfoEx(hSubSys,
				hInStream[0],
				NULL, &dwIStrBufferSize, NULL, NULL, NULL);
			printf("\nHPI_InStreamGetInfo %i: BufferSize=%d\n", 0,
				dwStrBufferSize);
			HandleError(err);
			err = HPI_FormatCreate(
				&FormatR,
				channels,
				format,
				samplerate,
				bitrate,
				0	// no attributes
				);
			HandleError(err);
			err = HPI_InStreamQueryFormat(hSubSys, hInStream[0],
				&FormatR);
			HandleError(err);

			dwDataSizeR = BLOCK_SIZE / 2;

			err = HPI_MixerGetControl(hSubSys,
				hMixer,
				0,
				0,
				HPI_DESTNODE_ISTREAM,
				0, HPI_CONTROL_METER, &hMeterControl);
			printf("HPI_MixerGetControl - PeakMeter: handle=%08X\n", hMeterControl);
			HandleError(err);
		}
	}
	////////////////////////////////////////////////////////////////////////////
	if (wNumOutStreams) {
		printf("********************************************************************\n");
		// open some audio files to play
		printf("Opening files \n");
		for (f = 0; f < nNumFiles; f++)
			THPI_WavFileOpen(f, szFile[f]);

		//check first available open stream to make sure we can play this format
		for (f = 0; f < nNumFiles; f++)
			if (hOutStream[f]) {
				printf("HPI_OutStreamQueryFormat\n");
				err = HPI_OutStreamQueryFormat(hSubSys,
					hOutStream[f], &FormatW);
				HandleError(err);
				break;
			}
		// get mixer meter control on out_stream0
		err = HPI_MixerGetControl(hSubSys,
			hMixer,
			HPI_SOURCENODE_OSTREAM,
			0, 0, 0, HPI_CONTROL_METER, &hMeterControl);
		printf("HPI_MixerGetControl - PeakMeter: handle=%08X\n",
			hMeterControl);
		HandleError(err);

		for (f = 1; f >= 0; f--) {
			// get mixer volume control on the connection
			// between out_stream0 and lineOut0
			err = HPI_MixerGetControl(hSubSys,
				hMixer,
				HPI_SOURCENODE_OSTREAM,
				f,
				HPI_DESTNODE_LINEOUT,
				f, HPI_CONTROL_VOLUME, &ahGainControl[f]
				);
			// HandleError( err );   Ignore the error, some adapters don't have this control

			if (err == 0) {
				printf("HPI_MixerGetControl - Volume: handle=%08X\n", ahGainControl[f]);
				// test - set gain on stream
				anGainLog[f][0] = 0;	//-4*f;
				anGainLog[f][1] = 0;	// -4*f-2;
				err = HPI_VolumeSetGain(hSubSys,
					ahGainControl[f], anGainLog[f]
					);
				HandleError(err);
			}
		}

	}

	{

		uint16_t wState = 0;
		uint32_t dwDataToPlay = 0;
		FILE *recfile[8];
		char recname[] = "rec0.pcm";

		///////////////////////////////////////////////////////////////////
		// preload buffer of stream
		if (wNumOutStreams) {
			printf("Preload\n");
			for (f = 0; f < nNumFiles; f++) {
				if (hOutStream[f]) {
					for (i = 0;
						i <
						((dwStrBufferSize -
								4) /
							dwDataSizeW); i++) {
						//printf("f=%d, i=%d,bs=%d,ds=%d\n",f,i,dwStrBufferSize,DataW.dwDataSize);
						// out_stream #1
						THPI_WavFileRead(f, abBuffer,
							dwDataSizeW);

						err = HPI_OutStreamWriteBuf
							(hSubSys,
							hOutStream[f],
							abBuffer, dwDataSizeW,
							&FormatW);
						HandleError(err);

						err = HPI_OutStreamGetInfoEx
							(hSubSys,
							hOutStream[f],
							&wState, NULL,
							&dwDataToPlay, NULL,
							NULL);
						HandleError(err);

						printf("[%i] D=%03d S=%d   \r", f, dwDataToPlay / 1000, wState);

					}
					printf("\n");
				}
			}

			///////////////////////////////////////////////////////////////////
			// start play back

			printf("********************************************************************\n");
			printf("HPI_OutStreamStart ");
			for (f = 0; f < nNumFiles; f++) {
				if (hOutStream[f]) {

					err = HPI_OutStreamStart(hSubSys,
						hOutStream[f]
						);
					HandleError(err);
					printf("%i ", f);
				}
			}
			printf("\n");
		}
		for (f = 0; f < wNumInStreams; f++) {
			printf("%d: ", f);

			if (instream_bbm) {
				printf("HPI_InStreamHostBufferAllocate ");
				err = HPI_InStreamHostBufferAllocate(hSubSys,
					hInStream[f], BLOCK_SIZE);
				if (err == HPI_ERROR_INVALID_FUNC)
					instream_bbm = 0;
				else
					HandleError(err);
			}

			printf("HPI_InStreamSetFormat ");
			err = HPI_InStreamSetFormat(hSubSys,
				hInStream[f], &FormatR);
			HandleError(err);

			printf("HPI_InStreamReset ");
			err = HPI_InStreamReset(hSubSys, hInStream[f]);
			HandleError(err);

			printf("HPI_InStreamStart \n");
			err = HPI_InStreamStart(hSubSys, hInStream[f]);
			HandleError(err);

			if (record_to_file) {
				if (szFile[f][0])
					recfile[f] = fopen(szFile[f], "w");
				else {
					recname[3] = f + '0';
					recfile[f] = fopen(recname, "w");
				}
			}
		}

		///////////////////////////////////////////////////////////////////
		// monitor state of streams and stop when empty

		// don't run anything if there are no in streams or out streams
		if ( wNumInStreams + wNumOutStreams == 0 )
			runtime = 1;

		while (--runtime) {

			int do_print = ((runtime % PRINT_INTERVAL) == 0);

			poll_delay();

			if (do_print && wNumInStreams)
				printf("IN :");
			for (f = 0; f < wNumInStreams; f++) {
				//get state of in stream
				err = HPI_InStreamGetInfoEx(hSubSys,
					hInStream[f],
					&wState,
					NULL, &dwDataToPlay, NULL, NULL);
				HandleError(err);

				if (do_print)
					printf("%03d %d ",
						dwDataToPlay / 1000, wState);

				if (dwDataToPlay >= dwDataSizeR) {
					dwDataToPlay = dwDataSizeR;

					err = HPI_InStreamReadBuf(hSubSys,
						hInStream[f],
						abBuffer, dwDataToPlay);

					HandleError(err);

					if (record_to_file)
						fwrite(abBuffer, dwDataToPlay,
							1, recfile[f]);
				}
			}
			if (do_print && wNumInStreams)
				printf("\n");

			if (do_print && wNumOutStreams)
				printf("OUT:");
			for (f = 0; f < nNumFiles; f++) {
				if (hOutStream[f]) {
					//get state of stream
					err = HPI_OutStreamGetInfoEx(hSubSys,
						hOutStream[f],
						&wState,
						NULL,
						&dwDataToPlay, NULL, NULL);
					HandleError(err);
					if (do_print)
						printf("%03d %d ",
							dwDataToPlay / 1000,
							wState);

					if ((dwStrBufferSize - dwDataToPlay) >
						dwDataSizeW) {
						THPI_WavFileRead(f, abBuffer,
							dwDataSizeW);
						err = HPI_OutStreamWriteBuf
							(hSubSys,
							hOutStream[f],
							abBuffer, dwDataSizeW,
							&FormatW);

						HandleError(err);
					}
				}
			}
			if (do_print && wNumOutStreams)
				printf("\n");
		}

		if (record_to_file)
			for (f = 0; f < wNumInStreams; f++)
				fclose(recfile[f]);

		printf("\n");
	}

	for (f = 0; f < nNumFiles; f++)
		THPI_WavFileClose(f);

	if (wNumOutStreams) {
		printf("HPI_OutStreamStop ");
		for (f = 0; f < nNumFiles; f++) {
			if (hOutStream[f]) {
				err = HPI_OutStreamStop(hSubSys,
					hOutStream[f]);
				printf("%i ", f);
				HandleError(err);
			}
		}

		printf("\nHPI_OutStreamClose ");
		for (f = 0; f < wNumOutStreams; f++) {
			if (hOutStream[f]) {
				if (outstream_bbm)
					err = HPI_OutStreamHostBufferFree
						(hSubSys, hOutStream[f]);

				err = HPI_OutStreamClose(hSubSys,
					hOutStream[f]);
				printf("%i ", f);
				HandleError(err);
			}
		}
	}

	if (wNumInStreams) {
		printf("\nHPI_InStreamStop \n");
		err = HPI_InStreamStop(hSubSys, hInStream[0]);
		HandleError(err);

		printf("\nHPI_InStreamClose ");
		for (f = 0; f < wNumInStreams; f++) {
			if (instream_bbm)
				err = HPI_InStreamHostBufferFree(hSubSys,
					hInStream[f]);

			err = HPI_InStreamClose(hSubSys, hInStream[f]);
			printf("%i ", f);
			HandleError(err);
		}
	}
#endif // HPI_BUILD_NO_STREAMS

	err = HPI_MixerClose(hSubSys, hMixer);
	printf("\nHPI_MixerClose\n");
	HandleError(err);

	err = HPI_AdapterClose(hSubSys, wAdapterIndex);
	printf("HPI_AdapterClose\n");
	HandleError(err);

	HPI_SubSysFree(hSubSys);

	return 0;
#endif
}

/******************************THPI_WavFileOpen **********************/
FILE *gpFile[8];

static void THPI_WavFileOpen(
	short nIndex,
	char *pszFile
)
{
	if (haveFiles) {
		printf("%s ", pszFile);
		gpFile[nIndex] = fopen(pszFile, "rb");
		if (!gpFile[nIndex]) {
			printf("****ERROR**** - can't open file\n");
			getch();
			exit(0);
		}
		if (toupper(pszFile[strlen(pszFile) - 1]) == 'S')
			fseek(gpFile[nIndex], 512, SEEK_SET);	//.DSS file
		else
			fseek(gpFile[nIndex], 0x50, SEEK_SET);	//.WAV file
	}
}

/******************************THPI_WavFileRead **********************/
static short THPI_WavFileRead(
	short nIndex,
	uint8_t *pbData,
	long lLength
)
{
	long lNumRead;
	long i = 0;
	uint16_t wSine;

	if (haveFiles) {
		lNumRead = fread(pbData, 1, lLength, gpFile[nIndex]);	//read WAV file
		if (lNumRead != lLength)
			return (1);
		else
			return (0);
	} else {
		// setup sinewave in buffer
		for (i = 0; i < lLength / 4; i++) {
			wSine = (uint16_t)(32767 * sin(2 * 3.141592653 * i / 32));
			pbData[(short)i * 4] = (uint8_t)wSine;	//left LSB
			pbData[(short)i * 4 + 1] = (uint8_t)(wSine >> 8);	//left MSB
			pbData[(short)i * 4 + 2] = (uint8_t)wSine;	//right LSB
			pbData[(short)i * 4 + 3] = (uint8_t)(wSine >> 8);	//right MSB
		}
		return (0);
	}
}

/******************************THPI_WavFileClose **********************/
static void THPI_WavFileClose(short nIndex)
{
	if (haveFiles)
		fclose(gpFile[nIndex]);
}

/****************************** HandleError **********************/
static void HandleError(hpi_err_t err)
{
	char szError[256];
	char nK = 0;

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("ERROR %d %s\n", err, szError);
		if (!yes) {
			printf("press Enter to continue, (q,Enter) to exit...\n");
			nK = getch();
			if (nK == 'q')
				exit(0);
		}
	}
}

static int getch(
	void
)
{
	return getchar();
}
/* END_OF_CODE */
