
import re
import numpy
from gmisclib import die
from gmisclib import wavio
from gmisclib import Numeric_gpk as NG

class BadChannel(ValueError):
	def __init__(self, *s):
		ValueError.__init__(self, *s)


NEARCLIP = 32500


	

def wav_read(filename, channel=None, info={}):
	"""Quick and dirty way to read a single channel of audio from a
	multichannel file.  If the channel is not specified, either as an
	argument or as info['Channel'], then it will pick whatever channel
	has the largest amplitude.   Needless to say, this is not entirely
	reliable unless you know that the other channel is silent.
	@param channel: which channel to use?  It can be a string name, an integer column number
		or None.
	@param info: a dictionary with miscellaneous metadata.   C{info['Channel']} is inspected.
	@type info: C{dict}.
	@type channel: C{string}, C{int}, or C{None}.
	@return: audio data with header information
		A gpk_img data structure holding the audio and header information.
		C{hdr['AudioChannel']}, C{hdr['Trouble']} and C{hdr['WhyAudioChannel']} are set.
	@rtype: gpkimgclass.gpk_img
	@except BadChannel: when you ask for a channel that isn't there.
	@note: Channel numbers start with zero!
	"""
	if channel is None:
		channel = info.get('Channel', None)

	x = wavio.read(filename)
	nch0 = x.d.shape[1]
	trouble = []
	if channel is None and x.d.shape[1] > 1:
		assert x.d.shape[1] < 1000
		rms = NG.block_stdev(x.d)
		channel = numpy.argmax(rms)
		x.d = x.d.take([channel], axis=1)
		assert len(x.d.shape) == 2
		die.info('Taking data from channel %d' % channel)
		if rms[1-channel] > 0.1*rms[channel]:
			die.warn('Warning: channel %d is comparable: MAD=%.2g/%.2g'
					% (1-channel, rms[1-channel], rms[channel])
					)
			x.hdr['WhyAudioChannel'] = 'It was slightly louder'
			trouble.append('Ambiguous channel')
		else:
			x.hdr['WhyAudioChannel'] = 'It was much louder'
	elif channel is None and x.d.shape[1]==1:
		x.hdr['WhyAudioChannel'] = 'There was only one'
		channel = 0
	elif channel=='mono':
		assert x.d.shape[1]==1
		x.hdr['WhyAudioChannel'] = 'There was only one'
		channel = 0
	elif 0 <= channel < x.d.shape[1]:
		x.d = x.d.take([channel], axis=1)
		x.hdr['WhyAudioChannel'] = 'Specified in g_audioprep.py'
	else:
		raise BadChannel, "Channel=%s but available channels are %s" % (channel, range(x.d.shape[1]))

	# x is now reduced to a single channel: 0.

	if NG.N_maximum(x.column(0)) > NEARCLIP or NG.N_minimum(x.column(0))< -NEARCLIP:
		da = numpy.absolute(x.column(0))
		die.warn("Data is near clipping: maximum abs(value)=%d, %.1f%% near clipping"
			% (NG.N_maximum(da), numpy.sum(numpy.greater(da, NEARCLIP))/float(da.shape[0])))
		trouble.append('Near clipping')

	x.hdr['Trouble'] = ','.join(trouble)
	assert 0 <= channel < nch0
	x.hdr['AudioChannel'] = channel
	assert x.d.shape[1] == 1
	return x


_nmparse = re.compile(r'(.*)\[([0-9_a-zA-Z]+)\]$')
def splitname(filename):
	"""Splits filenames in the form pathname[channel] into a pathname
	and a channel.
	"""
	m = _nmparse.match(filename)
	if m:
		name = m.group(1)
		try:
			channel = int(m.group(2))
		except ValueError:
			channel = m.group(2)
	else:
		name = filename
		channel = None
	return (name, channel)


def wav_read2(filename):
	"""Deal with filenames in the form path/name[channel].
	"""
	name, channel = splitname(filename)
	try:
		rv = wav_read(name, channel=channel, info={})
	except IOError, x:
		raise IOError(*(x.args + ('From dirty_io.wav_read2', 'Filename=%s' % filename)))
	return rv
