// -------------------------------------------------------------------------
//
//  Copyright (C) 2006-2011 Fons Adriaensen <fons@linuxaudio.org>
//    
//  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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// -------------------------------------------------------------------------


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include "adconf.h"
#include "sstring.h"


static const char *jackp   = "system:playback";

static const int chmap10 [] = { 0, 3, 1, -1 };
static const int chmap11 [] = { 0, 3, 1, 2, -1 };
static const int chmap20 [] = { 0, 3, 1, 8, 4, -1 };
static const int chmap21 [] = { 0, 3, 1, 2, 8, 4, -1 };
static const int chmap22 [] = { 0, 3, 1, 2, 6, 7, 5, 8, 4, -1 };
static const int chmap30 [] = { 0, 3, 1, 8, 4, 15, 9, -1 };
static const int chmap31 [] = { 0, 3, 1, 2, 8, 4, 15, 9, -1 };


static const float gc_n3d [16] =
{
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f,
    1.0f
};


static const float gc_sn3d [16] =
{
// Gain factors for SN3D -> N3D
    1.0f,
    1.732051f,
    1.732051f,
    1.732051f,
    2.236068f, 
    2.236068f, 
    2.236068f, 
    2.236068f, 
    2.236068f, 
    2.645751f,
    2.645751f,
    2.645751f,
    2.645751f,
    2.645751f,
    2.645751f,
    2.645751f
};


static const float gc_fuma [16] =
{
// Gain factors for FUMA -> N3D
    1.414214f,
    1.732051f,
    1.732051f,
    1.732051f,
    1.936492f,
    1.936492f,
    2.236068f,
    1.936492f,
    1.936492f,
    2.091650f,
    1.972026f,
    2.231093f,
    2.645751f,
    2.231093f,
    1.972026f,
    2.091650f,
};


Speaker::Speaker (void)
{
    reset ();
}


void Speaker::reset (void) 
{
    _r = 1.0f;
    _a = 0.0f;
    _e = 0.0f;
    _x = 1.0f;
    _y = 0.0f;
    _z = 0.0f;
    *_label = 0;
    *_jackp = 0;
}


void Speaker::rae2xyz (void)
{
    float a = RAD(_a);
    float e = RAD(_e);
    _x = _r * cosf (a) * cosf (e);
    _y = _r * sinf (a) * cosf (e);
    _z = _r * sinf (e);
}



Matrow::Matrow (void)
{
}


void Matrow::reset (void)
{
    memset (_coeff, 0, 16 * sizeof (float));
}


void Matrow::check (void)
{
    int   i;
    float s1, s2, s3;

    s1 = s2 = s3 = 0;
    for (i = 1; i <  4; i++) s1 += _coeff [i] * _coeff [i];
    for (i = 4; i <  9; i++) s2 += _coeff [i] * _coeff [i];
    for (i = 9; i < 16; i++) s3 += _coeff [i] * _coeff [i];
    printf ("%10.6lf %10.6lf %10.6lf %10.6lf\n", _coeff [0], s1, s2, s3);
}


void Matrow::encode (int cm, float x, float y, float z)
{
    float x2, y2, z2, c2, s2;

    reset ();
    if (cm & 1) _coeff [ACN_W] = 1.0f;
    if (cm & 2) _coeff [ACN_Y] = sqrtf (3.0f) * y;
    if (cm & 4) _coeff [ACN_Z] = sqrtf (3.0f) * z;
    if (cm & 8) _coeff [ACN_X] = sqrtf (3.0f) * x;
    cm >>= 4;
    if (! cm) return;
    x2 = x * x;
    y2 = y * y;
    z2 = z * z;
    c2 = (x2 - y2) / 2;
    s2 = x * y;
    if (cm &  1) _coeff [ACN_V] = sqrtf (15.0f) * s2;
    if (cm &  2) _coeff [ACN_T] = sqrtf (15.0f) * y * z;
    if (cm &  4) _coeff [ACN_R] = sqrtf (5.0f) * (1.5f * z2 - 0.5f);
    if (cm &  8) _coeff [ACN_S] = sqrtf (15.0f) * x * z;
    if (cm & 16) _coeff [ACN_U] = sqrtf (15.0f) * c2;
    cm >>= 5;
    if (! cm) return;
    if (cm &  1) _coeff [ACN_Q] = sqrtf (35.0f / 8)  * (3 * x2 - y2) * y;
    if (cm &  2) _coeff [ACN_O] = sqrtf (105.0f) * s2 * z;
    if (cm &  4) _coeff [ACN_M] = sqrtf (21.0f / 8)  * (5 * z2 - 1) * y; 
    if (cm &  8) _coeff [ACN_K] = sqrtf (7.0f / 4)   * (5 * z2 - 3) * z;
    if (cm & 16) _coeff [ACN_L] = sqrtf (21.0f / 8)  * (5 * z2 - 1) * x; 
    if (cm & 32) _coeff [ACN_N] = sqrtf (105.0f) * c2 * z;
    if (cm & 64) _coeff [ACN_P] = sqrtf (35.0f / 8)  * (x2 - 3 * y2) * x;
}


void Matrow::encode (int cm, float az, float el)
{
    float x, y, z;

    x = cosf (el) * cosf (az);
    y = cosf (el) * sinf (az);
    z = sinf (el);
    encode (cm, x, y, z);
}



AD_conf::AD_conf (void)
{
    _state     = 0;
    _fname [0] = 0;
    reset ();
}


void AD_conf::reset (void)
{
    int i;

    _fform = 3;
    _descr [0] = 0;
    _dec.cmask = 0;
    _dec.nband = 1;
    _dec.nspkr = 4;
    _dec.scale = FUMA;
    _opt.scale = FUMA;
    _opt.nfeff = NONE;
    _opt.delay = OFF;
    _opt.level = OFF;
    _opt.xfreq = 500;
    _opt.ratio = 0;
    for (i = 0; i < 4; i++)
    {
	_lfgain [i] = 1.0f;
	_hfgain [i] = 1.0f;
    }
    for (i = 0; i < MAXOP; i++)
    {
	_speakers [i].reset ();
	_lfmatrix [i].reset ();
	_hfmatrix [i].reset ();
    }
    _h_ord = 0;
    _v_ord = 0;
    _chmap = 0;
}


int AD_conf::save (const char *file)
{
    FILE          *F;
    Speaker       *S;
    char          *p;
    time_t        t;
    int           i;

    strcpy (_fname, file);
    p = strrchr (_fname, '.');
    if (!p) strcat (_fname, ".ambdec");
    if (! (F = fopen (_fname, "w"))) 
    {
	fprintf (stderr, "Can't open '%s' for writing\n", _fname);
        return 1;
    } 

    _fform = 3;
    t = time (0);
    fprintf (F, "# AmbDec configuration\n");
    fprintf (F, "# Written by %s-%s at %s\n", PROGNAME, VERSION, ctime (&t));
    fprintf (F, "/description      %s\n\n", _descr);
    fprintf (F, "/version          %d\n\n", _fform);
    fprintf (F, "/dec/chan_mask    %x\n", _dec.cmask); 
    fprintf (F, "/dec/freq_bands   %d\n", _dec.nband); 
    fprintf (F, "/dec/speakers     %d\n", _dec.nspkr); 
    switch (_dec.scale)
    {
    case N3D:	fprintf (F, "/dec/coeff_scale  n3d\n");  break;
    case SN3D: 	fprintf (F, "/dec/coeff_scale  sn3d\n"); break;
    case FUMA:	fprintf (F, "/dec/coeff_scale  fuma\n"); break;
    }
    fprintf (F, "\n"); 
    switch (_opt.scale)
    {
    case N3D: 	fprintf (F, "/opt/input_scale  n3d\n");  break;
    case SN3D: 	fprintf (F, "/opt/input_scale  sn3d\n"); break;
    case FUMA:	fprintf (F, "/opt/input_scale  fuma\n"); break;
    }
    switch (_opt.nfeff)
    {
    case NONE: 	 fprintf (F, "/opt/nfeff_comp   none\n");   break;
    case INPUT:	 fprintf (F, "/opt/nfeff_comp   input\n");  break;
    case OUTPUT: fprintf (F, "/opt/nfeff_comp   output\n"); break;
    }
    switch (_opt.delay)
    {
    case OFF: 	 fprintf (F, "/opt/delay_comp   off\n"); break;
    case ON:	 fprintf (F, "/opt/delay_comp   on\n");  break;
    }
    switch (_opt.level)
    {
    case OFF: 	 fprintf (F, "/opt/level_comp   off\n"); break;
    case ON:	 fprintf (F, "/opt/level_comp   on\n");  break;
    }
    fprintf (F, "/opt/xover_freq   %4.0lf\n", _opt.xfreq); 
    fprintf (F, "/opt/xover_ratio  %4.1lf\n", _opt.ratio); 

    fprintf (F, "\n/speakers/{\n"); 
    for (i = 0, S = _speakers; i < _dec.nspkr; i++, S++)
    {
	fprintf (F, "add_spkr   %3s  %8.3lf %8.1lf %8.1lf    %s\n", S->_label, S->_r, S->_a, S->_e, S->_jackp);  
    }
    fprintf (F, "/}\n\n");

    if (_dec.nband == 1) save_matrix (F, "matrix", _lfmatrix, _lfgain);
    else
    {
	save_matrix (F, "lfmatrix", _lfmatrix, _lfgain);
	save_matrix (F, "hfmatrix", _hfmatrix, _hfgain);
    }

    fprintf (F, "\n/end\n");
    fclose (F);
    return 0;
}


void AD_conf::save_matrix (FILE *F, const char *pref, Matrow *matr, float *gain)
{
    int           i, j, m;
    Matrow        *M;
    const float   *gc;
 
    switch (_dec.scale)
    {
    case N3D:
	gc = gc_n3d;
	break;
    case SN3D:
	gc = gc_sn3d;
	break;
    case FUMA:
	gc = gc_fuma;
	break;
    default:
	gc = 0;
    }

    fprintf (F, "/%s/{\n", pref);
    fprintf (F, "order_gain    %8.5lf %8.5lf %8.5lf %8.5lf\n", gain [0], gain [1], gain [2], gain [3]);

    for (j = 0, M = matr; j < _dec.nspkr; j++, M++)
    {
        fprintf (F, "add_row   ");
	for (i = 0, m = _dec.cmask; i < 16; i++, m >>= 1)
	{
	    if (m & 1) fprintf (F, " %9.6lf", M->_coeff [i] * gc [i]);
	}
	fprintf (F, "\n");
    }
    fprintf (F, "/}\n\n");
}


int AD_conf::load (const char *file)
{
    FILE          *F;
    Speaker       *S;
    Matrow        *R;
    float         *G;
    char          *p, *q;
    char          buff [1024];
    int           line, stat, n, r;
    int           i1, nsp, nlf, nhf, *cnt;
    float         f1, f2, f3;
    char          s1 [64];

    strcpy (_fname, file);
    p = strrchr (_fname, '.');
    if (!p) strcat (_fname, ".ambdec");
    if (! (F = fopen (_fname, "r"))) 
    {
	fprintf (stderr, "Can't open '%s' for writing\n", _fname);
        return 1;
    } 

    reset ();
    stat = 0;
    line = 0;
    nsp = 0;
    nlf = 0;
    nhf = 0;
    cnt = 0;
    S = 0;
    R = 0;
    G = 0;

    while (! stat && fgets (buff, 1024, F))
    {
        line++;
        p = buff; 
        while (isspace (*p)) p++;
        if (*p == '#' || *p < ' ') continue;
        q = p;
        while ((*q >= ' ') && !isspace (*q)) q++;
        *q++ = 0;   
        while ((*q >= ' ') && isspace (*q)) q++;

        stat = EXTRA;  
        if (!strcmp (p, "/version"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%d%n", &i1, &n) != 1) stat = ARGS;
	    else
	    {
		q += n;
		if ((i1 < 1) && (i1 > 3))
		{
		    fprintf (stderr, "Line %d: unknown version %d.\n", line, i1);
		    stat = ERROR;
		}
		else _fform = i1;
	    }
	}
	else if (!strcmp (p, "/description"))
	{
	    q [strlen (q) - 1] = 0;
	    strcpy (_descr, q);
	    stat = 0;
	}
        else if (!strcmp (p, "/dec/hor_order"))
	{
	    if (R || S) stat = SCOPE;
	    else if (_fform > 2) stat = COMM;
	    else if (sscanf (q, "%d%n", &i1, &n) != 1) stat = ARGS;
	    else
	    {
		q += n;
		if ((i1 < 1) || (i1 > 3))
		{
		    fprintf (stderr, "Line %d: illegal horizontal order %d.\n", line, i1);
		    stat = ERROR;
		}
		else _h_ord = i1;
	    }
	}
        else if (!strcmp (p, "/dec/ver_order"))
	{
	    if (R || S) stat = SCOPE;
	    else if (_fform > 2) stat = COMM;
	    else if (sscanf (q, "%d%n", &i1, &n) != 1) stat = ARGS;
	    else
	    {
 	        q += n;
  	        if ((i1 < 0) || (i1 > _h_ord) || (i1 + _h_ord > 4))
		{
		    fprintf (stderr, "Line %d: illegal vertical order %d.\n", line, i1);
		    stat = ERROR;
		}
                else _v_ord = i1;
	    }

	}
        else if (!strcmp (p, "/dec/chan_mask"))
	{
	    if (R || S) stat = SCOPE;
	    else if (_fform != 3) stat = COMM;
	    else if (sscanf (q, "%x%n", &i1, &n) != 1) stat = ARGS;
	    else
	    {
	       q += n;
               _dec.cmask = i1;
	    }
	}
        else if (!strcmp (p, "/dec/speakers"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%d%n", &i1, &n) != 1) stat = ARGS;
	    else
	    {
		q += n;
		if ((i1 < 4) || (i1 > MAXOP))
		{
		    fprintf (stderr, "Line %d: illegal number of speakers %d.\n", line, i1);
		    stat = ERROR;
		}
		else _dec.nspkr = i1;
	    }
	}
        else if (!strcmp (p, "/dec/freq_bands"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%d%n", &i1, &n) != 1) stat = ARGS;
	    else
	    {
		q += n;
		if ((i1 != 1) && (i1 != 2))
		{
		    fprintf (stderr, "Line %d: illegal number of frequency bands %d.\n", line, i1);
		    stat = ERROR;
		}
		else _dec.nband = i1;
	    }
	}
        else if (!strcmp (p, "/dec/coeff_scale"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%s%n", s1, &n) != 1) stat = ARGS;
  	    q += n;
	    if      (!strcmp (s1, "n3d"))   _dec.scale = N3D;
	    else if (!strcmp (s1, "sn3d"))  _dec.scale = SN3D;
	    else if (!strcmp (s1, "fuma"))  _dec.scale = FUMA;
	    else if (!strcmp (s1, "norm"))  _dec.scale = N3D;   // deprecated
	    else if (!strcmp (s1, "fmset")) _dec.scale = FUMA;  // deprecated
	    else
	    {
		fprintf (stderr, "Line %d: illegal matrix scale '%s'.\n", line, s1);
  	        stat = ERROR;
	    }
	}
        else if (!strcmp (p, "/opt/input_scale"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%s%n", s1, &n) != 1) stat = ARGS;
  	    q += n;
	    if      (!strcmp (s1, "n3d"))   _opt.scale = N3D;
	    else if (!strcmp (s1, "sn3d"))  _opt.scale = SN3D;
	    else if (!strcmp (s1, "fuma"))  _opt.scale = FUMA;
	    else if (!strcmp (s1, "norm"))  _opt.scale = N3D;   // deprecated
	    else if (!strcmp (s1, "fmset")) _opt.scale = FUMA;  // deprecated
	    else
	    {
		fprintf (stderr, "Line %d: illegal input scale '%s'.\n", line, s1);
   	        stat = ERROR;
	    }
	}
        else if (!strcmp (p, "/opt/nfeff_comp"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%s%n", s1, &n) != 1) stat = ARGS;
  	    q += n;
	    if      (!strcmp (s1, "none"))   _opt.nfeff = NONE;
	    else if (!strcmp (s1, "input"))  _opt.nfeff = INPUT;
	    else if (!strcmp (s1, "output")) _opt.nfeff = OUTPUT;
	    else
	    {
		fprintf (stderr, "Line %d: illegal NF compensation option '%s'.\n", line, s1);
 	        stat = ERROR;
	    }
	}
        else if (!strcmp (p, "/opt/delay_comp"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%s%n", s1, &n) != 1) stat = ARGS;
  	    q += n;
	    if      (!strcmp (s1, "off")) _opt.delay = OFF;
	    else if (!strcmp (s1, "on"))  _opt.delay = ON;
	    else
	    {
		fprintf (stderr, "Line %d: illegal delay compensation option '%s'.\n", line, s1);
  	        stat = ERROR;
	    }
	}
        else if (!strcmp (p, "/opt/level_comp"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%s%n", s1, &n) != 1) stat = ARGS;
  	    q += n;
	    if      (!strcmp (s1, "off")) _opt.level = OFF;
	    else if (!strcmp (s1, "on"))  _opt.level = ON;
	    else
	    {
		fprintf (stderr, "Line %d: illegal level compensation option '%s'.\n", line, s1);
 	        stat = ERROR;
	    }
	}
        else if (!strcmp (p, "/opt/xover_freq"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%f%n", &f1, &n) != 1) stat = ARGS;
	    else
	    {
		q += n;
		if ((f1 < 50) || (f1 > 5000))
		{
		    fprintf (stderr, "Line %d: illegal crossover frequency %4.0lf.\n", line, f1);
		    stat = ERROR;
		}
		else _opt.xfreq = f1;
	    }
	}
        else if (!strcmp (p, "/opt/xover_ratio"))
	{
	    if (R || S) stat = SCOPE;
	    if (sscanf (q, "%f%n", &f1, &n) != 1) stat = ARGS;
	    else
	    {
		q += n;
		if ((f1 < -30) || (f1 > 30))
		{
		    fprintf (stderr, "Line %d: illegal HF band gain %4.1lf.\n", line, f1);
		    stat = ERROR;
		}
		else _opt.ratio = f1;
	    }
	}
        else if (!strcmp (p, "/speakers/{"))
	{
            if (R || S) stat = SCOPE;
	    if (nsp) 
	    {
		fprintf (stderr, "Line %d: repeated speaker definition block\n", line);
 	        stat = ERROR;
	    }
	    else S = _speakers;
	}
        else if (!strcmp (p, "add_spkr"))
	{
	    if (!S) stat = SCOPE;
	    if (nsp == _dec.nspkr)
	    {
		fprintf (stderr, "Line %d: too many speaker definitions.\n", line);
		stat = ERROR;
	    }
	    else
	    {
		r = sscanf (q, "%s%f%f%f%n", s1, &f1, &f2, &f3, &n);
		q += n;
		if (r < 4) stat = ARGS;
		else if (strlen (s1) > 3)
		{    
		    fprintf (stderr, "Line %d: speaker name '%s' is too long.\n", line, s1);
		    stat = ERROR;
		}
		else if (f1 < 0.5f) 
		{
		    fprintf (stderr, "Line %d: illegal speaker distance.\n", line);
		    stat = ERROR;
		}
		else if ((f2 < -360) || (f2 > 360))
		{
		    fprintf (stderr, "Line %d: illegal speaker azimuth.\n", line);
		    stat = ERROR;
		}
		else if ((f3 < -90) || (f3 > 90))
		{
		    fprintf (stderr, "Line %d: illegal speaker elevation.\n", line);
		    stat = ERROR;
		}
		else
		{
		    strcpy (S->_label, s1);
		    S->_r = f1;
		    S->_a = f2;
		    S->_e = f3;
		    n = sstring (q, S->_jackp, 64);
		    q += n;
  	            S++;
		    nsp++;
		}
	    }
	}
        else if (!strcmp (p, "/matrix/{"))
	{
	    if (R || S) stat = SCOPE;
	    if (_dec.nband == 2)
	    {
		fprintf (stderr, "Line %d: 'matrix' definition in dual band decoder.\n", line);
		stat = ERROR;
	    }
	    if (nlf)
	    {
		fprintf (stderr, "Line %d: repeated matrix definition.\n", line);
		stat = ERROR;
	    }
            else
	    {
		R = _lfmatrix;
		G = _lfgain;
		cnt = &nlf;
		if ((_fform < 3) && (_chmap == 0)) makechmap ();
	    }
	}
	else if (!strcmp (p, "/lfmatrix/{"))
	{
	    if (R || S) stat = SCOPE;
	    if (_dec.nband == 1)
	    {
		fprintf (stderr, "Line %d: 'lfmatrix' definition in single band decoder.\n", line);
		stat = ERROR;
	    }
	    if (nlf)
	    {
		fprintf (stderr, "Line %d: repeated matrix definition.\n", line);
		stat = ERROR;
	    }
            else
	    {
		R = _lfmatrix;
		G = _lfgain;
		cnt = &nlf;
		if ((_fform < 3) && (_chmap == 0)) makechmap ();
	    }
	}
        else if (!strcmp (p, "/hfmatrix/{"))
	{
	    if (R || S) stat = SCOPE;
	    if (_dec.nband == 1)
	    {
		fprintf (stderr, "Line %d: 'hfmatrix' definition in single band decoder.\n", line);
		stat = ERROR;
	    }
	    if (nhf)
	    {
		fprintf (stderr, "Line %d: repeated matrix definition.\n", line);
		stat = ERROR;
	    }
            else
	    {
		R = _hfmatrix;
		G = _hfgain;
		cnt = &nhf;
		if ((_fform < 3) && (_chmap == 0)) makechmap ();
	    }
	}
        else if (!strcmp (p, "order_gain"))
	{
	    if (_fform == 3)
	    {
	        if (sscanf (q, "%f%f%f%f%n\n", G + 0, G + 1, G + 2, G + 3, &n) != 4) stat = ARGS;
 	        else q += n;
	    }
	    else
	    {
  	        switch (_h_ord) 
	        {
	    case 1:
	        if (sscanf (q, "%f%f%n\n", G + 0, G + 1, &n) != 2) stat = ARGS;
 	        else q += n;
		break;
	    case 2:
	        if (sscanf (q, "%f%f%f%n\n", G + 0, G + 1, G + 2, &n) != 3) stat = ARGS;
 	        else q += n;
		break;
	    case 3:
	        if (sscanf (q, "%f%f%f%f%n\n", G + 0, G + 1, G + 2, G + 3, &n) != 4) stat = ARGS;
 	        else q += n;
		break;
	        }
	    }
	}
        else if (!strcmp (p, "add_row"))
	{
	    if (!R) stat = SCOPE;
	    if (*cnt == _dec.nspkr)
	    {
		fprintf (stderr, "Line %d: too many matrix rows.\n", line);
		stat = ERROR;
	    }
            else
	    {
		if (_fform < 3) stat = read_matrow2 (&q, R);
		else            stat = read_matrow3 (&q, R);
	        if (!stat)
	        {
		    R++;
		    (*cnt)++;
	        }
	    }		
	}
        else if (!strcmp (p, "/}"))
	{
	    if      (S) S = 0;
	    else if (R) R = 0;
	    else stat = SCOPE;
	}
        else if (!strcmp (p, "/end"))
	{
	    if (R || S) stat = SCOPE;
	}
        else stat = COMM;

        if (stat == EXTRA)
	{
            while (isspace (*q)) q++;
            if (*q < ' ') stat = 0;
	}		     

        switch (stat)
	{
        case COMM:
	    fprintf (stderr, "Line %d: unknown command '%s'\n", line, p);   
            break;
        case ARGS:
	    fprintf (stderr, "Line %d: missing arguments in '%s' command\n", line, p);   
            break;
        case EXTRA:
	    fprintf (stderr, "Line %d: extra arguments in '%s' command\n", line, p);   
            break;
        case SCOPE:
	    fprintf (stderr, "Line %d: command '%s' in wrong scope\n", line, p);   
            break;
	}
    }

    fclose (F);
    return stat;
}


void AD_conf::makechmap (void)
{
    switch ((_h_ord << 4) | _v_ord)
    {
    case 0x10:
	_chmap = chmap10;
	_dec.cmask = 0x000B;
        break;
    case 0x11:
	_chmap = chmap11;
	_dec.cmask = 0x000F;
        break;
    case 0x20:
	_chmap = chmap20;
	_dec.cmask = 0x011B;
        break;
    case 0x21:
	_chmap = chmap21;
	_dec.cmask = 0x011F;
        break;
    case 0x22:
	_chmap = chmap22;
	_dec.cmask = 0x01FF;
        break;
    case 0x30:
	_chmap = chmap30;
	_dec.cmask = 0x831B;
        break;
    case 0x31:
	_chmap = chmap31;
	_dec.cmask = 0x831F;
        break;
    }
}


int AD_conf::read_matrow2 (char **q, Matrow *R)
{
    int     i, j, n, r;
    float   v;

    R->reset ();
    for (i = 0; i < 16; i++)
    {
	j = _chmap [i];
	if (j < 0) break;
        r = sscanf (*q, "%f%n", &v, &n);
        *q += n;
        if (r) R->_coeff [j] = v;
        else return ARGS;
    }
    if (_dec.scale == SN3D) for (i = 0; i < 16; i++) R->_coeff [i] /= gc_sn3d [i];
    if (_dec.scale == FUMA) for (i = 0; i < 16; i++) R->_coeff [i] /= gc_fuma [i];
    return 0;
} 


int AD_conf::read_matrow3 (char **q, Matrow *R)
{
    int     i, m, n, r;
    float   v;

    R->reset ();
    for (i = 0, m = _dec.cmask; m; i++, m >>= 1)
    {
	if (m & 1)
	{
	    r = sscanf (*q, "%f%n", &v, &n);
            *q += n;
	    if (r) R->_coeff [i] = v;
	    else return ARGS;
	}
    }
    if (_dec.scale == SN3D) for (i = 0; i < 16; i++) R->_coeff [i] /= gc_sn3d [i];
    if (_dec.scale == FUMA) for (i = 0; i < 16; i++) R->_coeff [i] /= gc_fuma [i];
    return 0;
} 


void AD_conf::default_label (void)
{
    for (int i = 0; i < _dec.nspkr; i++) sprintf (_speakers [i]._label, "%d", i + 1);
}


void AD_conf::default_jackp (void)
{
    for (int i = 0; i < _dec.nspkr; i++) sprintf (_speakers [i]._jackp, "%s_%d", jackp, i + 1);
}

