src/util.js
function parseArgs (audio, time, duration, options) {
	// no args at all
	if (time == null && duration == null && options == null) {
		options = {}
		time = 0
		duration = audio.duration
	}
	// single arg
	else if (time != null && duration == null && options == null) {
		// {}
		if (typeof time !== 'number') {
			options = time
			time = 0
			duration = audio.duration
		}
		// number
		else {
			options = {}
			duration = audio.duration
		}
	}
	// two args
	else if (time != null && duration != null && options == null) {
		// 1, 1
		if (typeof duration === 'number') {
			options = {}
		}
		// 1, {}
		else if (typeof duration != 'number') {
			options = duration
			duration = audio.duration
		}
	}

	if (typeof time !== 'number') throw Error('Bad argument `time`')

	if (time == null) time = 0
	if (duration == null) duration = audio.duration

	if (!time && duration < 0) time = -0;

	options = pick(options, {
		channel: 'channel ch numberOfChannel',
		channels: 'channels channelMap',
		destination: 'destination dest dst target out output container',
		start: 'start startTime fromTime',
		end: 'end endTime toTime',
		from: 'from offset fromOffset startOffset',
		to: 'to toOffset endOffset',
		duration: 'duration time',
		length: 'length number',
		format: 'format dtype dataFormat dataType type',
		keep: 'keep cut keepFragment return',
		copy: 'copy cpy cp clone',
		left: 'left beginning begin head',
		right: 'right ending end tail',
		threshold: 'threshold level lvl tol db',
		value: 'value val',
		rotate: 'rotate circular'
	})

	// ensure channels
	if (options.channel != null) {
		options.channels = options.channel
	}
	if (typeof options.channels === 'number') {
		options.channels = [options.channels]
	}
	if (options.channels == null) {
		let channels = options.channels || audio.channels
		options.channels = []
		for (let i = 0; i < channels; i++) {
			options.channels.push(i)
		}
	}

	if (!Array.isArray(options.channels)) {
		throw Error('Bad `channels` argument')
	}

	// take over from/to params
	// FIXME: reconsider these params
	if (options.from != null) time = options.from
	if (options.to != null) duration = options.to - time
	if (options.length != null) duration = options.length * audio.sampleRate
	if (options.duration != null) duration = options.duration

	// detect raw interval
	if (options.start == null) {
		let startOffset = Math.floor(time * audio.sampleRate)
		startOffset = nidx(startOffset, audio.buffer.length)
		options.start = startOffset
	}
	if (options.end == null) {
		let len = Math.ceil(duration * audio.sampleRate)

		let endOffset;
		if (len < 0) {
			endOffset = nidx(options.start + len, audio.buffer.length)
		}
		else {
			endOffset = Math.min(options.start + len, audio.buffer.length)
		}
		options.end = endOffset
	}

	// provide full options
	if (options.length == null) options.length = options.end - options.start
	if (options.from == null) options.from = options.start / audio.sampleRate
	if (options.to == null) options.to = options.end / audio.sampleRate
	if (options.duration == null) options.duration = options.length / audio.sampleRate

	return options
}
function parseArgs (audio, time, duration, options) {
	// no args at all
	if (time == null && duration == null && options == null) {
		options = {}
		time = 0
		duration = audio.duration
	}
	// single arg
	else if (time != null && duration == null && options == null) {
		// {}
		if (typeof time !== 'number') {
			options = time
			time = 0
			duration = audio.duration
		}
		// number
		else {
			options = {}
			duration = audio.duration
		}
	}
	// two args
	else if (time != null && duration != null && options == null) {
		// 1, 1
		if (typeof duration === 'number') {
			options = {}
		}
		// 1, {}
		else if (typeof duration != 'number') {
			options = duration
			duration = audio.duration
		}
	}

	if (typeof time !== 'number') throw Error('Bad argument `time`')

	if (time == null) time = 0
	if (duration == null) duration = audio.duration

	if (!time && duration < 0) time = -0;

	options = pick(options, {
		channel: 'channel ch numberOfChannel',
		channels: 'channels channelMap',
		destination: 'destination dest dst target out output container',
		start: 'start startTime fromTime',
		end: 'end endTime toTime',
		from: 'from offset fromOffset startOffset',
		to: 'to toOffset endOffset',
		duration: 'duration time',
		length: 'length number',
		format: 'format dtype dataFormat dataType type',
		keep: 'keep cut keepFragment return',
		copy: 'copy cpy cp clone',
		left: 'left beginning begin head',
		right: 'right ending end tail',
		threshold: 'threshold level lvl tol db',
		value: 'value val',
		rotate: 'rotate circular'
	})

	// ensure channels
	if (options.channel != null) {
		options.channels = options.channel
	}
	if (typeof options.channels === 'number') {
		options.channels = [options.channels]
	}
	if (options.channels == null) {
		let channels = options.channels || audio.channels
		options.channels = []
		for (let i = 0; i < channels; i++) {
			options.channels.push(i)
		}
	}

	if (!Array.isArray(options.channels)) {
		throw Error('Bad `channels` argument')
	}

	// take over from/to params
	// FIXME: reconsider these params
	if (options.from != null) time = options.from
	if (options.to != null) duration = options.to - time
	if (options.length != null) duration = options.length * audio.sampleRate
	if (options.duration != null) duration = options.duration

	// detect raw interval
	if (options.start == null) {
		let startOffset = Math.floor(time * audio.sampleRate)
		startOffset = nidx(startOffset, audio.buffer.length)
		options.start = startOffset
	}
	if (options.end == null) {
		let len = Math.ceil(duration * audio.sampleRate)

		let endOffset;
		if (len < 0) {
			endOffset = nidx(options.start + len, audio.buffer.length)
		}
		else {
			endOffset = Math.min(options.start + len, audio.buffer.length)
		}
		options.end = endOffset
	}

	// provide full options
	if (options.length == null) options.length = options.end - options.start
	if (options.from == null) options.from = options.start / audio.sampleRate
	if (options.to == null) options.to = options.end / audio.sampleRate
	if (options.duration == null) options.duration = options.length / audio.sampleRate

	return options
}
calc start, end, length and channels params from options