docs: function Generator (fn, opts) {
  	if (fn instanceof Function) {
  		if (typeof opts === 'number') {
  			opts = {duration: opts};
  		}
  		else {
  			opts = opts || {};
  		}
  		opts.generate = fn;
  	}
  	else {
  		opts = fn || {};
  	}
  
  	//sort out arguments
  	opts = extend({
  		//total duration of a stream
  		duration: Infinity,
  
  		//time repeat period, in seconds, or 1/frequency
  		period: Infinity,
  
  		//inferred from period
  		//frequency: 0,
  
  		/**
  		 * Generate sample value for a time.
  		 * Returns [L, R, ...] or a number for each channel
  		 *
  		 * @param {number} time current time
  		 */
  		generate: Math.random
  	}, pcm.defaults, opts);
  
  	//align frequency/period
  	if (opts.frequency != null) {
  		opts.period = 1 / opts.frequency;
  	} else {
  		opts.frequency = 1 / opts.period;
  	}
  
  	let time = 0, count = 0;
  
  	return generate;
  
  	//return sync source/map
  	function generate (buffer) {
  		if (!buffer) buffer = util.create(opts.samplesPerFrame, opts.channels, opts.sampleRate);
  
  		//get audio buffer channels data in array
  		var data = util.data(buffer);
  
  		//enough?
  		if (time + buffer.length / opts.sampleRate > opts.duration) return null;
  
  		//generate [channeled] samples
  		for (var i = 0; i < buffer.length; i++) {
  			var moment = time + i / opts.sampleRate;
  
  			//rotate by period
  			if (opts.period !== Infinity) {
  				moment %= opts.period;
  			}
  
  			var gen = opts.generate(moment);
  
  			//treat null as end
  			if (gen === null) {
  				return gen;
  			}
  
  			//wrap number value
  			if (!Array.isArray(gen)) {
  				gen = [gen || 0];
  			}
  
  			//distribute generated data by channels
  			for (var channel = 0; channel < buffer.numberOfChannels; channel++) {
  				data[channel][i] = (gen[channel] == null ? gen[0] : gen[channel]);
  			}
  		}
  
  		//update counters
  		count += buffer.length;
  		time = count / opts.sampleRate;
  
  		return buffer;
  	}
  }