static char *parse_voice(char *p,
			 struct SYMBOL *s)
{
	int voice;
	char *error_txt = NULL;
	char *clef_name, *clef_middle, *clef_stlines, *clef_scale;
	char *p_octave, *p_cue, *p_map;
	signed char *p_stem;
static struct kw_s {
	char *name;
	short len;
	short index;
} kw_tb[] = {
	{"name=", 5, 0},
	{"nm=", 3, 0},
	{"subname=", 8, 1},
	{"sname=", 6, 1},
	{"snm=", 4, 1},
	{"merge", 5, 2},
	{"up", 2, 3},
	{"down", 4, 4},
	{"stem=", 5, 5},
	{"gstem=", 6, 6},
	{"auto", 4, 7},
	{"dyn=", 4, 8},
	{"lyrics=", 7, 9},
	{"scale=", 6, 10},
	{"gchord=", 7, 11},
	{0}
};
	struct kw_s *kw;

	/* save the parameters of the previous voice */
	curvoice->ulen = ulen;
	curvoice->microscale = microscale;

	if (voice_tb[0].id[0] == '\0') {
		switch (s->abc_prev->abc_type) {
		case ABC_T_EOLN:
		case ABC_T_NOTE:
		case ABC_T_REST:
		case ABC_T_BAR:
			/* the previous voice was implicit (after K:) */
			voice_tb[0].id[0] = '1';
			break;
		}
	}
	{
		char *id, sep;

		id = p;
		while (isalnum((unsigned char) *p) || *p == '_')
			p++;
		sep = *p;
		*p = '\0';
		if (voice_tb[0].id[0] == '\0') {
			voice = 0;			/* first voice */
		} else {
			for (voice = 0; voice <= nvoice; voice++) {
				if (strcmp(id, voice_tb[voice].id) == 0)
					goto found;
			}
			if (voice >= MAXVOICE) {
				syntax("Too many voices", id);
				voice--;
			}
		}
		nvoice = voice;
		strncpy(voice_tb[voice].id, id, sizeof voice_tb[voice].id - 1);
		voice_tb[voice].mvoice = voice;
	found:
		strcpy(s->u.voice.id, voice_tb[voice].id);
		*p = sep;
	}
	curvoice = &voice_tb[voice];
	s->u.voice.voice = voice;

	/* if in tune, set the voice parameters */
	if (parse.abc_state == ABC_S_TUNE) {
		ulen = curvoice->ulen;
		microscale = curvoice->microscale;
	}

	/* parse the other parameters */
	clef_name = clef_middle = clef_stlines = clef_scale = NULL;
	p_octave = p_cue = p_map = NULL;
	p_stem = &s->u.voice.stem;
	for (;;) {
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
		p = parse_extra(p, &clef_name, &clef_middle, &clef_stlines,
				&clef_scale, &p_octave, &p_cue, &p_map);
		if (*p == '\0')
			break;
		for (kw = kw_tb; kw->name; kw++) {
			if (strncmp(p, kw->name, kw->len) == 0)
				break;
		}
		if (!kw->name) {
			while (!isspace((unsigned char) *p) && *p != '\0')
				p++;	/* ignore unknown keywords */
			continue;
		}
		p += kw->len;
		switch (kw->index) {
		case 0:			/* name */
			p = get_str(tex_buf, p, TEX_BUF_SZ);
			s->u.voice.fname = getarena(strlen(tex_buf) + 1);
			strcpy(s->u.voice.fname, tex_buf);
			break;
		case 1:			/* subname */
			p = get_str(tex_buf, p, TEX_BUF_SZ);
			s->u.voice.nname = getarena(strlen(tex_buf) + 1);
			strcpy(s->u.voice.nname, tex_buf);
			break;
		case 2:			/* merge */
			s->u.voice.merge = 1;
			break;
		case 3:			/* up */
			*p_stem = 1;
			break;
		case 4:			/* down */
			*p_stem = -1;
			break;
		case 5:			/* stem= */
			p_stem = &s->u.voice.stem;
			break;
		case 6:			/* gstem= */
			p_stem = &s->u.voice.gstem;
			break;
		case 7:			/* auto */
			*p_stem = 2;
			break;
		case 8:			/* dyn= */
			p_stem = &s->u.voice.dyn;
			break;
		case 9:			/* lyrics= */
			p_stem = &s->u.voice.lyrics;
			break;
		case 10: {		/* scale= */
			float sc;

			sc = atof(p);
			if (sc >= 0.5 && sc <= 2)
				s->u.voice.scale = sc;
			else
				error_txt = "Bad value for voice scale";
			while (!isspace((unsigned char) *p) && *p != '\0')
				p++;
			break;
		    }
		case 11:		/* gchord= */
			p_stem = &s->u.voice.gchord;
			break;
		}
	}

	s->u.voice.octave = parse_octave(p_octave);
	if (p_cue) {
		if (strncmp(p_cue, "on", 2) == 0)
			s->u.voice.cue = 1;
		else
			s->u.voice.cue = -1;
	}
	if (clef_stlines)
		s->u.voice.stafflines = clef_stlines;
//	else
//		s->u.voice.stafflines = "|||||";
	if (clef_scale) {
		float sc;

		sc = atof(clef_scale);
		if (sc >= 0.5 && sc <= 3)
			s->u.voice.staffscale = sc;
		else
			syntax("Bad value of staffscale", clef_scale);
	}
	if (clef_name || clef_middle) {
		s = abc_new(ABC_T_CLEF, NULL);
		parse_clef(s, clef_name, clef_middle);
	}
	if (p_map) {
		strcpy(tex_buf, "%%voicemap ");
		get_str(&tex_buf[11], p_map, TEX_BUF_SZ - 12);
		abc_new(ABC_T_PSCOM, tex_buf);
	}
	return error_txt;
}