

def prep_phones(pa, pmap, phonology, is_manual_transcription=False,
			insert_optional_phones=False, datasource='?'):
	"""This maps from the string of phonemes that the
	human or dictionary produces into a string of
	C, V, S and transition phonemes that the ASR systems wants.
	It inserts opportunities for liason where possible.
	@param is_manual_transcription: are pauses transcribed?  Otherwise, the
		system assumes that they may appear at word boundaries.
	@type is_manual_transcription: bool
	@param pa: The transcribed phoneme sequence
	@type pa: C{list(str)}
	@param pmap: A mapping from input phonemes to output phonemes.
	@type pmap: C{dict(str:str)}
	"""
	#: the output list of phonemes gets accumulated here.
	phones = [GP.phone_c('sil', s=0.0)]
	#: are we in the middle of a garbled section?
	garble = False
	#: counters are just used for trouble detection.
	counters = dictops.dict_of_accums()
	#: Have we just seen an end-of-word marker?
	iwp = False
	last = GP.phone_c('#')
	for p in pa:
		try:
			pm = pmap[p.lbl]
		except KeyError:
			pm = None
		if p.lbl in ['#', '##', 'sil']:
			counters.add('sil', 1)
			if phones[-1].lbl != 'sil':
				phones.append(GP.phone_c('sil', s=p.s, e=p.e))
				last = GP.phone_c('#')
			iwp = False
		elif p.startswith('#w') or p.startswith('#p'):
			# Word or punctuation boundaries.
			garble = False
			if not phones[-1].lbl.startswith('IPP_'):
				iwp = True
		elif p.startswith('#['):
			# Beginning of garble/correction
			garble = True
		elif p.startswith('#]') or p.startswith(']#'):
			# end of garble/correction
			iwp = True
			garble = False
		elif p.startswith('#'):
			# Other non-phoneme markers.
			garble = False
		elif pm is not None and pm:
			# All normal phonemes.
			assert not (phones[-1].lbl=='sil' and pm=='sil')
			assert pm != 'sil'
			if iwp:
				counters.add('#w', 1)
			if garble:
				pm = 'garble'
			if iwp and insert_optional_phones:
				if isV(phonology, last.lbl) and isV(phonology, p.lbl):
					phones.append(GP.phone_c(['garble_C', 'sil'], optional=True))
					# print 'has #w VV'
				elif isC(phonology, last.lbl) and isC(phonology, p.lbl):
					# Epenthetic vowel insertion
					phones.append(GP.phone_c(['garble_V', 'sil'], optional=True))
					# print 'has #w CC'
				elif isC(phonology, last.lbl):
					# Consonant deletion
					phones[-1].optional = True
					# print 'has #w C#w'
					# A possible pause:
					phones.append(GP.phone_c('sil', optional=True))
				else:
					# A possible pause:
					phones.append(GP.phone_c('sil', optional=True))
			iwp = False
			phones.append(GP.phone_c(pm, s=p.s, e=p.e))
			last = p
			counters.add(pm, 1)
			counters.add('*', 1)
		elif pm is None:
			# Legal non-phonemes like tone markers.
			pass
		else:
			raise BadPhonesError("Unrecognized phone: %s" % p, datasource)
	if len(phones) < 3:
		raise BadPhonesError("Too few phones: %d"
					% counters.get('*',0),
					pa, datasource)
	if not is_manual_transcription and counters.get('#w', 0) < (counters.get('*',0)-7)//15:
		raise BadPhonesError("Too few word boundaries: %d in a string of %d phones"
					% (counters.get('#w',0), counters.get('*',0)),
					pa, datasource)
	if counters.get('garble',0) > (counters.get('*',0)+5)//10:
		raise BadPhonesError("Too many garbles: %d in a string of %d phones"
					% (counters.get('garble',0), counters.get('*',0)),
					pa, datasource)
	if phones[-1].lbl != 'sil':
		phones.append(GP.phone_c('sil', dur=FINALDUR))

	if not insert_optional_phones:
		fill_NaNs_in_phone_sequence(phones)
	return phones
