Bladeren bron

Replacing waveform calc

Noah 7 jaren geleden
bovenliggende
commit
c171b63765
3 gewijzigde bestanden met toevoegingen van 86 en 79 verwijderingen
  1. 4 30
      audiogram/index.js
  2. 12 49
      audiogram/waveform.js
  3. 70 0
      lib/waveform.js

+ 4 - 30
audiogram/index.js Bestand weergeven

@@ -5,7 +5,6 @@ var path = require("path"),
5 5
     serverSettings = require("../settings/"),
6 6
     transports = require("../lib/transports/"),
7 7
     logger = require("../lib/logger/"),
8
-    probe = require("./probe.js"),
9 8
     getWaveform = require("./waveform.js"),
10 9
     initializeCanvas = require("./initialize-canvas.js"),
11 10
     drawFrames = require("./draw-frames.js"),
@@ -29,32 +28,6 @@ function Audiogram(settings) {
29 28
 
30 29
 }
31 30
 
32
-// Probe an audio file for its duration and # of channels, compute the number of frames required
33
-Audiogram.prototype.probe = function(cb) {
34
-
35
-  var self = this;
36
-
37
-  this.status("probing");
38
-
39
-  probe(this.audioPath, function(err, data){
40
-
41
-    if (err) {
42
-      return cb(err);
43
-    }
44
-
45
-    if (self.settings.maxDuration && self.settings.maxDuration < data.duration) {
46
-      cb("Exceeds max duration of " + self.settings.maxDuration + "s");
47
-    }
48
-
49
-    self.set("numFrames", self.numFrames = Math.floor(data.duration * self.settings.framesPerSecond));
50
-    self.channels = data.channels;
51
-
52
-    cb(null);
53
-
54
-  });
55
-
56
-};
57
-
58 31
 // Get the waveform data from the audio file, split into frames
59 32
 Audiogram.prototype.getWaveform = function(cb) {
60 33
 
@@ -63,11 +36,12 @@ Audiogram.prototype.getWaveform = function(cb) {
63 36
   this.status("waveform");
64 37
 
65 38
   getWaveform(this.audioPath, {
66
-    channels: this.channels,
67
-    numFrames: this.numFrames,
68
-    samplesPerFrame: this.settings.samplesPerFrame
39
+    samplesPerFrame: this.settings.samplesPerFrame,
40
+    maxDuration: this.settings.maxDuration
69 41
   }, function(err, waveform){
70 42
 
43
+    self.set("numFrames", self.numFrames = self.settings.waveform.length);
44
+
71 45
     return cb(err, self.settings.waveform = waveform);
72 46
 
73 47
   });

+ 12 - 49
audiogram/waveform.js Bestand weergeven

@@ -1,62 +1,25 @@
1
-var d3 = require("d3"),
2
-    getPCM = require("../lib/pcm.js");
1
+var probe = require("../lib/probe.js"),
2
+    processWaveform = require("../lib/waveform.js");
3 3
 
4 4
 function getWaveform(filename, options, cb) {
5 5
 
6
-
7
-
8
-  options.numFrames
9
-  options.samplesPerFrame;
10
-
11
-  var waveformOptions = {
12
-    "scan": false,
13
-    "waveformjs": "-",
14
-    "wjs-width": numSamples,
15
-    "wjs-precision": 2,
16
-    "wjs-plain": true,
17
-    "encoding": "utf8"
18
-  };
19
-
20
-  waveform(filename, waveformOptions, function(err, buf) {
6
+  probe(filename, function(err, data) {
21 7
 
22 8
     if (err) {
23 9
       return cb(err);
24 10
     }
25 11
 
26
-    cb(null, processWaveform(JSON.parse(buf)));
27
-
28
-  });
29
-
30
-  // Slice one-dimensional waveform data into array of arrays, one array per frame
31
-  function processWaveform(waveformData) {
32
-
33
-    var max = -Infinity,
34
-        maxFrame;
35
-
36
-    waveformData.forEach(function(d, i){
37
-      if (d > max) {
38
-        max = d;
39
-        maxFrame = Math.floor(i / options.samplesPerFrame);
40
-      }
41
-    });
42
-
43
-    // Scale peaks to 1
44
-    var scaled = d3.scaleLinear()
45
-      .domain([0, max])
46
-      .range([0, 1]);
47
-
48
-    var waveformFrames = d3.range(options.numFrames).map(function getFrame(frameNumber) {
49
-
50
-      return waveformData.slice(options.samplesPerFrame * frameNumber, options.samplesPerFrame * (frameNumber + 1)).map(scaled);
51
-
52
-    });
53
-
54
-    // Set the first and last frame's waveforms to something peak-y for better thumbnails
55
-    waveformFrames[0] = waveformFrames[waveformFrames.length - 1] = waveformFrames[maxFrame];
12
+    if (options.maxDuration && options.maxDuration < data.duration) {
13
+      return cb("Exceeds max duration of " + options.maxDuration + "s");
14
+    }
56 15
 
57
-    return waveformFrames;
16
+    processWaveform(filename, {
17
+      numFrames: Math.floor(data.duration * options.samplesPerFrame),
18
+      samplesPerFrame: options.samplesPerFrame,
19
+      channels: data.channels
20
+    }, cb);
58 21
 
59
-  }
22
+  });
60 23
 
61 24
 }
62 25
 

+ 70 - 0
lib/waveform.js Bestand weergeven

@@ -0,0 +1,70 @@
1
+var pcm = require("./pcm.js"),
2
+    d3 = require("d3");
3
+
4
+function processWaveform(filename, options, cb) {
5
+
6
+  var stream = pcm(filename, {
7
+        channels: options.channels
8
+      }),
9
+      samples = [];
10
+
11
+  stream.on("data",function(sample, channel){
12
+
13
+    // Average multiple channels
14
+    if (channel > 0) {
15
+      samples[samples.length - 1] = ((samples[samples.length - 1] * channel) + sample) / (channel + 1);
16
+    } else {
17
+      samples.push(sample);
18
+    }
19
+
20
+  });
21
+
22
+  stream.on("error", cb);
23
+
24
+  stream.on("end", function(output){
25
+    var processed = processSamples(samples, options.numFrames, options.samplesPerFrame);
26
+    return cb(null, processed);
27
+  });
28
+
29
+}
30
+
31
+function processSamples(samples, numFrames, samplesPerFrame) {
32
+
33
+  // TODO spread out slop across frames
34
+  var perFrame = Math.floor(samples.length / numFrames),
35
+      perPoint = Math.floor(perFrame / samplesPerFrame),
36
+      range = d3.range(samplesPerFrame),
37
+      min = max = 0;
38
+
39
+  var unadjusted = d3.range(numFrames).map(function(frame){
40
+
41
+    var frameSamples = samples.slice(frame * perFrame, (frame + 1) * perFrame);
42
+
43
+    return range.map(function(point){
44
+
45
+      var pointSamples = frameSamples.slice(point * perPoint, (point + 1) * perPoint),
46
+          localMin = localMax = 0;
47
+
48
+      for (var i = 0, l = pointSamples.length; i < l; i++) {
49
+        localMin = Math.min(localMin, pointSamples[i]);
50
+        localMax = Math.min(localMax, pointSamples[i]);
51
+      }
52
+
53
+      min = Math.min(min, localMin);
54
+      max = Math.min(max, localMax);
55
+
56
+      return [localMin, localMax];
57
+
58
+    });
59
+
60
+  });
61
+
62
+  return unadjusted.map(function(frame){
63
+    return frame.map(function(point){
64
+      return [point[0] / min, point[1] / max];
65
+    });
66
+  });
67
+
68
+}
69
+
70
+module.exports = processWaveform;