Object Oriented programming is a paradigm where data is stored inside something and is modified or controlled by using methods of that object. JavaScript is an object-oriented programming language since, literally, everything is an object. Even the number 1 has methods associated with it! The JS-Xtract framework enables object oriented view-points by wrapping data up inside easier to use objects. Whilst these objects remove a certain amount of flexibility the procedural calls give, they also make it easier to handle the data.

There are 4 object prototypes which are of importance: `TimeData()`

, `SpectrumData()`

, `PeakSpectrumData()`

and `HarmonicSpectrumData()`

. All of these hold data for their given data types and define methods for extracting relevant features and transforming from one set to another. For instance calling `TimeData()->spectrum()`

returns a new `SpectrumData()`

object with the transformed data.

The `TimeData()`

has the following two constructors:

```
// Some Globals
var N = 1024;
var sample_rate = 44100.0;
// Creates a timeData with no sample_rate defined
// Automatically defines a data size of 1024 elements
var timeData = new TimeData(N);
// Creates a timeData with a sample_rate defined
// Automatically defines a data size of 1024 elements
var timeData2 = new TimeData(N,sample_rate);
```

If no sample_rate is set, then the sample_rate can be updated ONCE by calling `TimeData()->sample_rate = x`

. If the sample_rate has been set, then an error will be returned. This is because features and other objects may have references to this data at that sample rate.

The `SpectrumData()`

, `PeakSpectrumData()`

and `HarmonicSpectrumData()`

are effectively the same class chain and have the same constructors as the `TimeData()`

except with their relevant object names, ie: `new SpectrumData(N);`

. However if the sample_rate is undefined, then it defaults to being `Math.PI`

. This way the frequency list is actually the radial frequency from 0 to half Pi. The sample rate can again be updated using the same method (`SpectrumData()->sample_rate = x`

) but again only once.

The following tables shows how much memory is actually set asside for each constructor

Prototype | Underlying Memory | Size (N) | Bytes per N |
---|---|---|---|

`TimeData()` |
`Float64Array` |
N | 8 |

`SpectrumData()` |
`Float64Array` |
2N | 16 |

`PeakSpectrumData()` |
`Float64Array` |
2N | 16 |

`HarmonicSpectrumData()` |
`Float64Array` |
2N | 16 |

The spectrum objects have 2N number of elements because they also hold the center frequency of each bin.

Once an object has been created, the object can be populated by calling the `copyDataFrom()`

function.

```
var N = 1024;
var wave = new Float64Array(N);
// Assume a frame of the time domain information is in wave.
// Construct an empty TimeData object
var timeData = new TimeData(N, 44100.0);
// Copy the data in
timeData.copyDataFrom(wave, N, 0);
```

`copyDataFrom()`

takes three arguments: source array, number of elements to copy, the offset in the destination. The following show how to use those arguments to perform a multitude of copies

```
timeData.copyDataFrom(wave);
// Copies the data from wave up to either the length of timeData or the length of wave, whichever is sooner
// N = Math.min(wave.length, timeData.length)
timeData.copyDataFrom(wave, N);
// Copies the data from wave up to the number of N
// If N is bigger than either wave or timeData, then N = Math.min(wave.length, timeData.length);
timeData.copyDataFrom(wave, N, 8)
// Copies the data from wave up to the N element, storing them in timeData from the position 8.
// Since offset=8, then wave[0] is placed in timeData->_data[8].
// Again N is protected by using N = Math.min(wave.length, timeData.length+offset);
timeData.copyDataFrom(wave.subarray(12,N),N)
// Copies data from wave up to the N element, starting from index 12
// Actuall this is the same as the second call above, but we are effectively only passing in the wave array from element 12 onwards
// This way it is possible to perform the movement with a source offset.
```

In all of these cases, any unaffected elements are untouched! If we take the third example, where offset was at 8, then elements 0 through 7 are untouched and hold the data they previously held. This distinction allows TimeData to be used as a buffer or to update specific parts of the spectrum for instance. To clear the entire buffer, use `zeroData()`

or pass in an array of zeros.

For the spectrum data, the copied array only includes the magnitude bins, the frequency center bins are left untouched.

When performing any of the data movement functions, the result node is cleared.

When performing feature extractions, the results are stored inside the object as well as returned by the function call. For example:

```
var timeData = new TimeData(1024);
// Calculate the mean
timeData.mean();
timeData.results = {
'mean': 0.4352
}
```

In this example the mean is stored in the results. This is of a key importance when we reconsider the chaining issues from the procedural calls. Since the mean is stored, in the following example that value is simply re-used:

```
// Calculate the variance
timeData.variance();
timeData.results = {
'mean': 0.4352,
'variance': 0.009434;
}
```

However, the object oriented advantage appears if we did not calculate the mean at all:

```
var timeData = new TimeData(1024);
// Calculate the variance
timeData.variance();
timeData.results = {
'mean': 0.4352,
'variance': 0.009434;
}
```

Because the data is copied in the object it's state can be guaranteed, therefore when intermediary values are calculated they can be stored for as long as the data remains the same. Likewise objects can actually hold references to each other:

```
var timeData = new TimeData(1024, 44100.0);
// Perform the FFT
timeData.spectrum();
// The resulting transform is stored as a new SpectrumData object in the results
timeData.results = {
'spectrum': SpectrumData()
}
// Calculate the spectral centroid
timeData.results.spectrum.spectral_centroid();
// Display it in the SpectrumData object's result
timeData.results.spectrum.results = {
'spectral_centroid': 1343.032;
}
```

If the TimeData object is then updated, the result node is deleted. If the SpectrumData node is not referred to somewhere else then it is garbage collected and deleted.

For transmission or copying of the entire object a special `toJSON()`

funtion converts the result node to a JSON string. This function is on each TimeData, SpectrumData, PeakSpectrumData and HarmonicSpectrumData. There are also toJSON functions for Float32Array and Float64Array objects.

```
var timeData = new TimeData(1024, 44100.0);
timeData.variance();
var str = timeData.toJSON();
str = '{"mean": 0.4352, "variance": 0.009434}';
```