Passed
Push — master ( 29f5b0...4c43c3 )
by Rafael S.
02:58
created

lib/wavefile-tag-editor.js   A

Complexity

Total Complexity 22
Complexity/F 3.14

Size

Lines of Code 149
Function Count 7

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 68
dl 0
loc 149
rs 10
c 0
b 0
f 0
wmc 22
mnd 15
bc 15
fnc 7
bpm 2.1427
cpm 3.1427
noi 0

7 Functions

Rating   Name   Duplication   Size   Complexity  
A WaveFileTagEditor.listTags 0 13 3
A WaveFileTagEditor.setTag 0 25 3
A wavefile-tag-editor.js ➔ fixRIFFTag_ 0 10 4
A WaveFileTagEditor.deleteTag 0 9 2
A WaveFileTagEditor.getTag 0 8 2
A WaveFileTagEditor.getTagIndex_ 0 17 5
A WaveFileTagEditor.getLISTINFOIndex_ 0 11 3
1
/*
2
 * Copyright (c) 2017-2019 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview The WaveFileTagEditor class.
27
 * @see https://github.com/rochars/wavefile
28
 */
29
30
import { WaveFileCreator } from './wavefile-creator';
31
32
/**
33
 * A class to edit meta information in wav files.
34
 * @extends WaveFileCreator
35
 * @ignore
36
 */
37
export class WaveFileTagEditor extends WaveFileCreator {
38
39
  /**
40
   * Return the value of a RIFF tag in the INFO chunk.
41
   * @param {string} tag The tag name.
42
   * @return {?string} The value if the tag is found, null otherwise.
43
   */
44
  getTag(tag) {
45
    /** @type {!Object} */
46
    let index = this.getTagIndex_(tag);
47
    if (index.TAG !== null) {
48
      return this.LIST[index.LIST].subChunks[index.TAG].value;
49
    }
50
    return null;
51
  }
52
53
  /**
54
   * Write a RIFF tag in the INFO chunk. If the tag do not exist,
55
   * then it is created. It if exists, it is overwritten.
56
   * @param {string} tag The tag name.
57
   * @param {string} value The tag value.
58
   * @throws {Error} If the tag name is not valid.
59
   */
60
  setTag(tag, value) {
61
    tag = fixRIFFTag_(tag);
62
    /** @type {!Object} */
63
    let index = this.getTagIndex_(tag);
64
    if (index.TAG !== null) {
65
      this.LIST[index.LIST].subChunks[index.TAG].chunkSize =
66
        value.length + 1;
67
      this.LIST[index.LIST].subChunks[index.TAG].value = value;
68
    } else if (index.LIST !== null) {
69
      this.LIST[index.LIST].subChunks.push({
70
        chunkId: tag,
71
        chunkSize: value.length + 1,
72
        value: value});
73
    } else {
74
      this.LIST.push({
75
        chunkId: 'LIST',
76
        chunkSize: 8 + value.length + 1,
77
        format: 'INFO',
78
        subChunks: []});
79
      this.LIST[this.LIST.length - 1].subChunks.push({
80
        chunkId: tag,
81
        chunkSize: value.length + 1,
82
        value: value});
83
    }
84
  }
85
86
  /**
87
   * Remove a RIFF tag from the INFO chunk.
88
   * @param {string} tag The tag name.
89
   * @return {boolean} True if a tag was deleted.
90
   */
91
  deleteTag(tag) {
92
    /** @type {!Object} */
93
    let index = this.getTagIndex_(tag);
94
    if (index.TAG !== null) {
95
      this.LIST[index.LIST].subChunks.splice(index.TAG, 1);
96
      return true;
97
    }
98
    return false;
99
  }
100
101
  /**
102
   * Return a Object<tag, value> with the RIFF tags in the file.
103
   * @return {!Object<string, string>} The file tags.
104
   */
105
  listTags() {
106
    /** @type {?number} */
107
    let index = this.getLISTINFOIndex_();
108
    /** @type {!Object} */
109
    let tags = {};
110
    if (index !== null) {
111
      for (let i = 0, len = this.LIST[index].subChunks.length; i < len; i++) {
112
        tags[this.LIST[index].subChunks[i].chunkId] =
113
          this.LIST[index].subChunks[i].value;
114
      }
115
    }
116
    return tags;
117
  }
118
119
  /**
120
   * Return the index of the INFO chunk in the LIST chunk.
121
   * @return {?number} the index of the INFO chunk.
122
   * @private
123
   */
124
  getLISTINFOIndex_() {
125
    /** @type {?number} */
126
    let index = null;
127
    for (let i = 0, len = this.LIST.length; i < len; i++) {
128
      if (this.LIST[i].format === 'INFO') {
129
        index = i;
130
        break;
131
      }
132
    }
133
    return index;
134
  }
135
136
  /**
137
   * Return the index of a tag in a FILE chunk.
138
   * @param {string} tag The tag name.
139
   * @return {!Object<string, ?number>}
140
   *    Object.LIST is the INFO index in LIST
141
   *    Object.TAG is the tag index in the INFO
142
   * @private
143
   */
144
  getTagIndex_(tag) {
145
    /** @type {!Object<string, ?number>} */
146
    let index = {LIST: null, TAG: null};
147
    for (let i = 0, len = this.LIST.length; i < len; i++) {
148
      if (this.LIST[i].format == 'INFO') {
149
        index.LIST = i;
150
        for (let j=0, subLen = this.LIST[i].subChunks.length; j < subLen; j++) {
151
          if (this.LIST[i].subChunks[j].chunkId == tag) {
152
            index.TAG = j;
153
            break;
154
          }
155
        }
156
        break;
157
      }
158
    }
159
    return index;
160
  }
161
}
162
163
/**
164
 * Fix a RIFF tag format if possible, throw an error otherwise.
165
 * @param {string} tag The tag name.
166
 * @return {string} The tag name in proper fourCC format.
167
 * @private
168
 */
169
function fixRIFFTag_(tag) {
170
  if (tag.constructor !== String) {
171
    throw new Error('Invalid tag name.');
172
  } else if (tag.length < 4) {
173
    for (let i = 0, len = 4 - tag.length; i < len; i++) {
174
      tag += ' ';
175
    }
176
  }
177
  return tag;
178
}
179