Skip to content

Commit

Permalink
Added slice method
Browse files Browse the repository at this point in the history
See #84
  • Loading branch information
chrisn committed Dec 6, 2023
1 parent fe7bb04 commit 84fc4ea
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 2 deletions.
33 changes: 33 additions & 0 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* [.channel](#waveformDatachannelindex)
* [.resample](#waveformDataresampleoptions)
* [.concat](#waveformDataconcatwaveforms)
* [.slice](#waveformDatasliceoptions)
* [.toJSON](#waveformDatatojson)
* [.toArrayBuffer](#waveformDatatoarraybuffer)
* [WaveformDataChannel](#waveformdatachannel)
Expand Down Expand Up @@ -468,6 +469,38 @@ console.log(wave3.length); // -> 100
console.log(combinedResult.length); // -> 900
```

### waveformData.slice(options)

Returns a subset of the waveform data between a given start and end point.

#### Arguments

| Name | Type |
| --------- | ------------------------------------ |
| `options` | An object containing either `startIndex` and `endIndex` values, which give the start and end indexes in the waveform data, or `startTime` and `endTime`, which give the start and end times (in seconds) |

#### Example

Return the waveform between index 100 and index 200:

```javascript
const waveform = WaveformData.create(buffer);

const slice = waveform.slice({ startIndex: 100, endIndex: 200 });

console.log(slice.length); // -> 100
```

Return the waveform between 1.0 and 2.0 seconds:

```javascript
const waveform = WaveformData.create(buffer);

const slice = waveform.slice({ startTime: 1.0, endTime: 2.0 });

console.log(slice.length); // -> 86
```

## waveformData.toJSON()

Returns an object containing the waveform data.
Expand Down
64 changes: 64 additions & 0 deletions src/waveform-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,70 @@ WaveformData.prototype = {
return totalBuffer;
},

slice: function(options) {
var startIndex = 0;
var endIndex = 0;

if (options.startIndex != null && options.endIndex != null) {
startIndex = options.startIndex;
endIndex = options.endIndex;
}
else if (options.startTime != null && options.endTime != null) {
startIndex = this.at_time(options.startTime);
endIndex = this.at_time(options.endTime);
}

if (startIndex < 0) {
throw new RangeError("startIndex or startTime must not be negative");
}

if (endIndex < 0) {
throw new RangeError("endIndex or endTime must not be negative");
}

if (startIndex > this.length) {
startIndex = this.length;
}

if (endIndex > this.length) {
endIndex = this.length;
}

if (startIndex > endIndex) {
startIndex = endIndex;
}

var length = endIndex - startIndex;

var header_size = 24; // Version 2
var bytes_per_sample = this.bits === 8 ? 1 : 2;
var total_size = header_size
+ length * 2 * this.channels * bytes_per_sample;

var output_data = new ArrayBuffer(total_size);
var output_dataview = new DataView(output_data);

output_dataview.setInt32(0, 2, true); // Version
output_dataview.setUint32(4, this.bits === 8, true); // Is 8 bit?
output_dataview.setInt32(8, this.sample_rate, true);
output_dataview.setInt32(12, this.scale, true);
output_dataview.setInt32(16, length, true);
output_dataview.setInt32(20, this.channels, true);

for (var i = 0; i < length * this.channels * 2; i++) {
var sample = this._at(startIndex * this.channels * 2 + i);

if (this.bits === 8) {
output_dataview.setInt8(header_size + i, sample);
}
else {
output_dataview.setInt16(header_size + i * 2, sample, true);
}
}

return new WaveformData(output_data);
},

/**
* Returns the data format version number.
*/
Expand Down
94 changes: 92 additions & 2 deletions test/unit/waveform-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default function waveformDataTests(WaveformData) {
context("with " + bits + "-bit " + format + " data", function() {
beforeEach(function() {
const data = format === "binary" ? getBinaryData({ channels: 1, bits: bits })
: getJSONData({ channels: 1, bits: bits });
: getJSONData({ channels: 1, bits: bits });

instance = WaveformData.create(data);
});
Expand Down Expand Up @@ -284,6 +284,96 @@ export default function waveformDataTests(WaveformData) {
expect(result.duration).to.equal(expectations.duration * 3);
});
});

describe(".slice()", function() {
context("with valid startIndex and endIndex", function() {
it("should return a subset of the waveform", function() {
var extract = instance.slice({ startIndex: 1, endIndex: 4 });

expect(extract.length).to.equal(3);
expect(extract.channels).to.equal(instance.channels);
expect(extract.bits).to.equal(instance.bits);
expect(extract.channel(0).min_array()).to.deep.equal([-10, 0, -5]);
expect(extract.channel(0).max_array()).to.deep.equal([10, 0, 7]);
});
});

context("with endIndex beyond the length of the waveform", function() {
it("should limit to the end of the waveform", function() {
var extract = instance.slice({ startIndex: 1, endIndex: 12 });

expect(extract.length).to.equal(9);
expect(extract.channels).to.equal(instance.channels);
expect(extract.bits).to.equal(instance.bits);
expect(extract.channel(0).min_array()).to.deep.equal([-10, 0, -5, -5, 0, 0, 0, 0, -2]);
expect(extract.channel(0).max_array()).to.deep.equal([10, 0, 7, 7, 0, 0, 0, 0, 2]);
});
});

context("with startIndex equal to endIndex", function() {
it("should return a zero length waveform", function() {
var extract = instance.slice({ startIndex: 1, endIndex: 1 });

expect(extract.length).to.equal(0);
expect(extract.channels).to.equal(instance.channels);
expect(extract.bits).to.equal(instance.bits);
expect(extract.channel(0).min_array()).to.deep.equal([]);
expect(extract.channel(0).max_array()).to.deep.equal([]);
});
});

context("with startIndex greater than endIndex", function() {
it("should return a zero length waveform", function() {
var extract = instance.slice({ startIndex: 1, endIndex: 1 });

expect(extract.length).to.equal(0);
expect(extract.channels).to.equal(instance.channels);
expect(extract.bits).to.equal(instance.bits);
expect(extract.channel(0).min_array()).to.deep.equal([]);
expect(extract.channel(0).max_array()).to.deep.equal([]);
});
});

context("with startIndex and endIndex beyond the length of the waveform", function() {
it("should return a zero length waveform", function() {
var extract = instance.slice({ startIndex: 10, endIndex: 12 });

expect(extract.length).to.equal(0);
expect(extract.channels).to.equal(instance.channels);
expect(extract.bits).to.equal(instance.bits);
expect(extract.channel(0).min_array()).to.deep.equal([]);
expect(extract.channel(0).max_array()).to.deep.equal([]);
});
});

context("with negative startIndex", function() {
it("should throw RangeError", function() {
expect(function() {
instance.slice({ startIndex: -1, endIndex: 4 });
}).to.throw(RangeError);
});
});

context("with negative endIndex", function() {
it("should throw RangeError", function() {
expect(function() {
instance.slice({ startIndex: 1, endIndex: -1 });
}).to.throw(RangeError);
});
});

context("with valid startTime and endTime", function() {
it("should return a subset of the waveform", function() {
var extract = instance.slice({ startTime: instance.time(1), endTime: instance.time(4) });

expect(extract.length).to.equal(3);
expect(extract.channels).to.equal(instance.channels);
expect(extract.bits).to.equal(instance.bits);
expect(extract.channel(0).min_array()).to.deep.equal([-10, 0, -5]);
expect(extract.channel(0).max_array()).to.deep.equal([10, 0, 7]);
});
});
});
});
});
});
Expand All @@ -295,7 +385,7 @@ export default function waveformDataTests(WaveformData) {
context("with " + bits + "-bit " + format + " data", function() {
beforeEach(function() {
const data = format === "binary" ? getBinaryData({ channels: 2, bits: bits })
: getJSONData({ channels: 2, bits: bits });
: getJSONData({ channels: 2, bits: bits });

instance = WaveformData.create(data);
});
Expand Down

0 comments on commit 84fc4ea

Please sign in to comment.