
// nodejs child_process
var childProcess = require('child_process');

/**
 * 
 * analyzer constructor
 * 
 * @returns {analyzerConstructor}
 */
var analyzer = function analyzerConstructor() {
    
};

/**
 * 
 * get track data using ffprobe (channels, samplerate, ...)
 * 
 * @param {type} trackPath
 * @param {type} callback
 * @returns {undefined}
 */
analyzer.prototype.getData = function getDataFunction(trackPath, callback) {
    
    // ffprobe file data
    var ffprobeSpawn = childProcess.spawn(
       'ffprobe',
       [
           trackPath,
           '-v',
           'quiet',
           '-show_streams',
           '-show_format',
           '-print_format',
           'json'
       ]
    );

    var stdoutOuputString = '';
    var stderrOuputString = '';

    // ffprobe recieves data on stdout
    ffprobeSpawn.stdout.on('data', function(data) {

        stdoutOuputString += data;
        
    });
    
    var trackData = {};

    ffprobeSpawn.stdout.on('end', function(data) {

        //console.log('ffprobeSpawn stdout end');

        if (stdoutOuputString !== '') {

            //console.log('stdoutOuput');
            //console.log(stdoutOuputString);
            
            // parse the ffprobe json string response
            var stdoutOuput = JSON.parse(stdoutOuputString);
            
            if (Object.keys(stdoutOuput).length > 0) {

                // create a trackdata object with the informations we need
                trackData.duration = stdoutOuput['format']['duration'];
                trackData.size = stdoutOuput['format']['size'];
                trackData.bitRate = stdoutOuput['format']['bit_rate'];
                trackData.sampleRate = stdoutOuput['streams'][0]['sample_rate'];
                trackData.channels = stdoutOuput['streams'][0]['channels'];
                
            }

        }

    });

    ffprobeSpawn.stderr.setEncoding('utf8');

    ffprobeSpawn.stderr.on('data', function(data) {

        stderrOuputString += data;

    });

    ffprobeSpawn.stderr.on('end', function() {

        //console.log('ffprobeSpawn stderr end');

    });

    ffprobeSpawn.on('exit', function(code) {

        //console.log('ffprobeSpawn exit, code: ' + code);
        
        // if the code is an error code
        if (code > 0) {
            
            if (stderrOuputString === '') {
                
                stderrOuputString = 'unknown ffprobe error';
                
            }
            
            callback(stderrOuputString);
            
        } else {
            
            // if the trackdata object isnt empty
            if (Object.keys(trackData).length > 0) {
                
                callback(false, trackData);
                
            } else {
                
                callback('ffprobe did not output any data');
                
            }
            
        }

    });

    ffprobeSpawn.on('close', function() {

        //console.log('ffprobeSpawn close');

    });
    
};

/**
 * 
 * get pcm data of a track
 * 
 * @param {type} trackPath
 * @param {type} peaksAmountRaw
 * @param {type} callback
 * @returns {undefined}
 */
analyzer.prototype.getPeaks = function getValuesFunction(trackPath, peaksAmountRaw, callback) {
    
    this.getData(trackPath, function(error, trackData) {

        if (!error) {
            
            //console.log('ffprobe track data: ');
            //console.log(trackData);
            
            if (peaksAmountRaw !== undefined) {
            
                var peaksAmount = parseInt(peaksAmountRaw);
                
            } else {
                
                callback('peaksAmount is undefined');
                
            }
            
            // get audio pcm as 16bit little endians
            var ffmpegSpawn = childProcess.spawn(
                'ffmpeg',
                [
                    '-i',
                    trackPath,
                    '-f',
                    's16le',
                    '-ac',
                    //trackData.channels,
                    // put everything into one channel
                    1,
                    '-acodec',
                    'pcm_s16le',
                    '-ar',
                    trackData.sampleRate,
                    '-y',
                    'pipe:1' // pipe to stdout
                ]
            );

            var stdoutOuputString = '';
            var stderrOuputString = '';
            
            var samples = [];

            ffmpegSpawn.stdout.on('data', function(buffer) {
                
                // each buffer contains a certain amount of bytes (8bit)
                // https://trac.ffmpeg.org/wiki/audio%20types

                // and we convert them to signed 16-bit little endians
                // http://nodejs.org/api/buffer.html#buffer_buf_readint16le_offset_noassert
                
                
                var i;
                var dataLen = buffer.length;
                
                // each buffer data contains 8bit but we read 16bit so i += 2
                for (i = 0; i < dataLen; i += 2) {

                    var positiveSample = Math.abs(buffer.readInt16LE(i, false));

                    samples.push(positiveSample);

                }

            });

            ffmpegSpawn.stdout.on('end', function(data) {

                //console.log('ffmpegSpawn stdout end');
                
                var samplesLength = samples.length;

                // check if we got enough samples to put at least one sample
                // into each peak
                if (samplesLength > peaksAmount) {

                    // calculate how much samples we have to put into one peak
                    var samplesCountPerPeak = Math.floor(samplesLength / peaksAmount);
                    var peaks = [];
                    var peaksInPercent = [];

                    var i;
                    var start = 0;
                    var end = start + samplesCountPerPeak;
                    var highestPeak = 0;

                    // build as much peaks as got requested
                    for (i = 0; i < peaksAmount; i++) {
                        
                        // get a series of samples collection
                        var peaksGroup = samples.slice(start, end);
                        var x;
                        var samplesSum = 0;
                        var peaksGroupLength = peaksGroup.length;

                        // merge the samples into a single peak
                        for (x = 0; x < peaksGroupLength; x++) {

                            samplesSum += peaksGroup[x];

                        }

                        peaks.push(samplesSum);
                        
                        // find the highest peak
                        if (samplesSum > highestPeak) {
                            
                            highestPeak = samplesSum;
                            
                        }

                        start += samplesCountPerPeak;
                        end += samplesCountPerPeak;

                    }
                    
                    var y;
                    var peaksLength = peaks.length;

                    // convert the peaks into percantage values
                    for (y = 0; y < peaksLength; y++) {

                        var peakInPercent = Math.round((peaks[y] / highestPeak) * 100);

                        peaksInPercent.push(peakInPercent);

                    }

                    callback(false, peaksInPercent);

                } else {
                    
                    if (samplesLength === 0) {

                        callback('no output recieved');
                        
                    } else if (samplesLength < peaksAmount) {
                        
                        callback('not enough peaks in this song for a full wave');
                        
                    }

                }

            });

            ffmpegSpawn.stderr.setEncoding('utf8');

            ffmpegSpawn.stderr.on('data', function(data) {

                //console.log(data.toString());

            });

            ffmpegSpawn.stderr.on('end', function() {

                //console.log('ffmpegSpawn stderr end');

            });

            ffmpegSpawn.on('exit', function(code) {

                //console.log('ffmpegSpawn exit, code: ' + code);

            });

            ffmpegSpawn.on('close', function() {

                //console.log('ffmpegSpawn close');

            });
            
        } else {
            
            callback(error);
            
        }
        
    });
    
};

module.exports.analyzer = analyzer;
@@ -2983,24 +2983,111 @@
 n close');%0A%0A
+    %7D);%0A    %0A    ffprobeSpawn.on('error', function(error) %7B%0A%0A        callback(error);%0A%0A
     %7D);%0A    
@@ -8635,24 +8635,142 @@
 n close');%0A%0A
+            %7D);%0A            %0A            ffmpegSpawn.on('error', function(error) %7B%0A%0A                callback(error);%0A%0A
             

handle ffprobe and ffmpeg not in path errors