/************************************************
  hdsptester.c (based on alsa playback examples)
  version: 0.2

  tests alsa-output of a rme hdsp-card 
  via 440hz testtone on all available channels

  successfully used with 
	rme hdsp + multiface (rev.0xa)
	and rme hdsp9652 + adi-8 AE

  compile with:
  gcc hdsptester.c -o hdsptester -lm -lasound

  (c) 2003 dieb13 AT klingt.org 
  no warranty, use it for whatever you want ;)
  download: http://pd.klingt.org/files
**************************************************/
  
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <math.h>
#include <alsa/asoundlib.h>

/* edit this to change samplerate
   or use another pcm-device ... */
#define SAMPLERATE 44100
#define BLOCKSIZE 4096
#define PERIODS 2
#define PCM_NAME "hw:0"

snd_pcm_t *pcm_handle;

void dac_shutdown (int sig){
        snd_pcm_drop(pcm_handle);
        snd_pcm_close(pcm_handle);
        fprintf (stdout, "got signal - shutting down\n");
        exit(0);
}

int main(int argc, char *argv[]){
	int pcmreturn;
        void **bufptrs;
        long *buf;
	int bs = BLOCKSIZE;
	int nc = 26;
	int rate = SAMPLERATE;
	int periods = PERIODS;
	int periodsize ;
	float sinval = 0.;
        float sininc = 6.2838 * 440. / (float)SAMPLERATE ;
	snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
        snd_pcm_hw_params_t *hwparams;
        int err = 0;
        int i = 0;
        int exact_rate;   /* Sample rate returned by */
        int dir;          /* exact_rate == rate --> dir = 0 */

        char *pcm_name;
        pcm_name = strdup(PCM_NAME);
        snd_pcm_hw_params_malloc(&hwparams);

/*      fprintf (stdout, "allocate done\n");*/

        if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0) {
                fprintf(stderr, "Error opening PCM device %s\n", pcm_name);
                return(-1);
        }


/*        fprintf (stdout, "opening done\n");*/

        if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
                fprintf(stderr, "Can not configure this PCM device.\n");
                return(-1);
        }

/*      fprintf (stdout, "any done\n");*/

        if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_NONINTERLEAVED) < 0) {
                fprintf(stderr, "Error setting access.\n");
                return(-1);
        }

/*      fprintf (stdout, "setting access done\n");*/

        if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S32_LE) < 0) {
                fprintf(stderr, "Error setting format.\n");
                return(-1);
        }

/*      fprintf (stdout, "setting format done\n");*/

        exact_rate = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, &dir);
        if (dir != 0) {
                fprintf(stderr, "The rate %d Hz is not supported \n  ==> Using %d Hz instead. (dir=%d)\n",
                      rate, exact_rate,dir);
        }

/*      fprintf (stdout, "set rate near done\n");*/


        if (snd_pcm_hw_params_test_channels (pcm_handle, hwparams, nc)){
                for (i=0;i < 65;i++){
                        if (snd_pcm_hw_params_test_channels (pcm_handle, hwparams, i) == 0){
/*                              fprintf(stderr, "can_channels:%d\n",i);*/
                                nc = i;
                        }
                }       
        }
	if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams,nc) < 0) {
		fprintf(stderr, "Error setting number of channels to %d.\n",i);
		return(-1);	
	}

/*      fprintf (stdout, "setting channels done\n");*/

                
        if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0) < 0) {
                fprintf(stderr, "Error setting periods.\n");
                return(-1);
        }

        periodsize = bs;
        if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, periodsize  * periods) < 0) {
                fprintf(stderr, "Error setting buffersize.\n");
                return(-1);
        }       
        

/*      fprintf (stdout, "setting periods + buffersize done\n");*/

        if ((err = snd_pcm_hw_params (pcm_handle, hwparams)) < 0) {
                fprintf (stderr, "cannot set parameters (%s)\n",
                        snd_strerror (err));
                 return(-1);
        }

        fprintf (stdout, "soundsetup done\n");

/*
        void dac_shutdown(int sig);
        if (signal (SIGTERM, dac_shutdown) == SIG_IGN)
            	signal (SIGTERM, SIG_IGN);
	if (signal (SIGINT, dac_shutdown) == SIG_IGN)
		signal (SIGTERM, SIG_IGN);
	if (signal (SIGQUIT, dac_shutdown) == SIG_IGN)
		signal (SIGTERM, SIG_IGN);
*/
        bufptrs = malloc( nc * sizeof(long*));
        buf = malloc(bs * sizeof(long));

        for (i=0; i < nc;i++){
                *(bufptrs + i) = (long*)(buf);
        }
	

	fprintf (stdout, "starting playback on %d channels\n",nc);
        while (1){
                for (i=0; i <  bs;i++){
                        sinval += sininc;
                        *(buf + i) = (long)(sin(sinval) * 32767. * 65535.);
                        if (sinval > 6.2838){sinval -= 6.2838;}
                }
		
                while ((pcmreturn = snd_pcm_writen(pcm_handle,bufptrs,bs)) < 0) {
                        snd_pcm_prepare(pcm_handle);
                        fprintf(stderr, "hdsptester: buffer underrun\n");
                }
        }

        free (bufptrs);
        free (buf);
}
