To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / WAVE.js

History | View | Annotate | Download (4.87 KB)

1
// Decode and perform WAVE file byte level manipulation
2

    
3
find_subarray = function(arr,subarr) {
4
    var arr_length = arr.length;
5
    var subarr_length = subarr.length;
6
    var last_check_index = arr_length - subarr_length;
7
    
8
    positionLoop:
9
    for (var i=0; i <= last_check_index; i++)
10
    {
11
        for (var j=0; j< subarr_length; j++)
12
        {
13
            if (arr[i + j] !== subarr[j]) {
14
                continue positionLoop;
15
            }
16
        }
17
        return i;
18
    }
19
    return -1;
20
};
21

    
22
function convertToInteger(arr) {
23
    var value = 0;
24
    for (var i=0; i<arr.length; i++)
25
    {
26
        value += arr[i]<<(i*8);
27
    }
28
    return value;
29
}
30
function convertToString(arr) {
31
    var str = "";
32
    for (var i=0; i<arr.length; i++)
33
    {
34
        str = str.concat(String.fromCharCode(arr[i]));
35
    }
36
    return str;
37
}
38

    
39
function WAVE()
40
{
41
    // The WAVE file object
42
    this.status == 'WAVE_DECLARED'
43
    
44
    this.decoded_data = null;
45
    
46
    this.RIFF = String(); //ChunkID
47
        this.size; //ChunkSize
48
        this.FT_Header; //Format
49
        this.fmt_marker; //Subchunk1ID
50
        this.formatDataLength; //Subchunk1Size
51
        this.type; //AudioFormat
52
        this.num_channels; //NumChannels
53
        this.sample_rate; //SampleRate
54
        this.byte_rate; //ByteRate
55
        this.block_align; //BlockAlign
56
        this.bits_per_sample; //BitsPerSample
57
        this.data_header; //Subchunk2ID
58
        this.data_size; //Subchunk2Size
59
    this.num_samples;
60
    
61
    this.open = function(IOArrayBuffer)
62
    {
63
        var IOView8 = new Uint8Array(IOArrayBuffer);
64
        this.RIFF = convertToString(IOView8.subarray(0,4));
65
        if (this.RIFF != 'RIFF')
66
        {
67
            console.log('WAVE ERR - Not a RIFF file');
68
            return 1;
69
        }
70
        this.size = convertToInteger(IOView8.subarray(4,8));
71
        this.FT_Header = convertToString(IOView8.subarray(8,12));
72
        this.fmt_marker = convertToString(IOView8.subarray(12,16));
73
        this.formatDataLength = convertToInteger(IOView8.subarray(16,20));
74
        this.type = convertToInteger(IOView8.subarray(20,22));
75
        this.num_channels = convertToInteger(IOView8.subarray(22,24));
76
        this.sample_rate = convertToInteger(IOView8.subarray(24,28));
77
        this.byte_rate = convertToInteger(IOView8.subarray(28,32));
78
        this.block_align = convertToInteger(IOView8.subarray(32,34));
79
        this.bits_per_sample = convertToInteger(IOView8.subarray(34,36));
80
        
81
        // Find the data header first
82
        var data_start = find_subarray(IOView8,[100, 97, 116, 97]);
83
        
84
        this.data_header = convertToString(IOView8.subarray(data_start,data_start+4));
85
        this.data_size = convertToInteger(IOView8.subarray(data_start+4,data_start+8));
86
        
87
        this.num_samples = this.data_size / this.block_align;
88
        
89
        this.decoded_data = [];
90
        if (this.type != 1 && this.type != 3) {
91
            console.log("Neither PCM nor IEEE float, cannot decode");
92
            return 1;
93
        }
94
        for (var c=0; c<this.num_channels; c++)
95
        {
96
            this.decoded_data.push(new Float32Array(this.num_samples));
97
        }
98
        var sampleDataOffset = data_start+8;
99
        
100
        // Now need to decode the data from sampleDataOffset
101
        // Data is always interleved
102
        var data_view;
103
        if (this.type == 3)
104
        {
105
            // Already in float
106
            if (this.bits_per_sample == 32) {
107
                data_view = new Float32Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size));
108
            } else if (this.bits_per_sample == 64) {
109
                data_view = new Float64Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size));
110
            }
111
        } else if (this.type == 1)
112
        {
113
            data_view = new Float32Array(this.num_samples*this.num_channels);
114
            integerConvert(new Uint8Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size)),data_view,this.bits_per_sample/8);
115
        }
116
        deInterlace(data_view,this.decoded_data);
117
        return 0;
118
    };
119
}
120

    
121
function deInterlace(src_array, dst_array)
122
{
123
    var number = src_array.length;
124
    var channels = dst_array.length;
125
    var channel_index = 0;
126
    var dst_index = 0;
127
    for (var n=0; n<number; n++)
128
    {
129
        dst_array[channel_index][dst_index] = src_array[n];
130
        channel_index++;
131
        if (channel_index >= channels) {
132
            channel_index = 0;
133
            dst_index++;
134
        }
135
    }
136
}
137

    
138
function integerConvert(srcView,dstView,srcBytes)
139
{
140
    //Convert integers of a Uint8Array of certain byte length into a Float32Array
141
    var number = dstView.length;
142
    var outBits = srcBytes*8;
143
    var endShift = 32 - outBits;
144
    if (srcView.length != dstView.length*srcBytes)
145
    {
146
        return -1;
147
    }
148
    for (var n=0; n<number; n++)
149
    {
150
        var srcIndex = n*srcBytes;
151
        var intData = convertToInteger(srcView.subarray(srcIndex,srcIndex+srcBytes));
152
        intData = (intData << (endShift));
153
        dstView[n] = intData / 2147483648;
154
    }
155
}