/*
* Copyright (c) 2017-2019 Rafael da Silva Rocha.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/**
* @fileoverview The WaveFileTagEditor class.
* @see https://github.com/rochars/wavefile
*/
import { WaveFileCreator } from './wavefile-creator';
/**
* A class to edit meta information in wav files.
* @extends WaveFileCreator
* @ignore
*/
export class WaveFileTagEditor extends WaveFileCreator {
/**
* Return the value of a RIFF tag in the INFO chunk.
* @param {string} tag The tag name.
* @return {?string} The value if the tag is found, null otherwise.
*/
getTag(tag) {
/** @type {!Object} */
let index = this.getTagIndex_(tag);
if (index.TAG !== null) {
return this.LIST[index.LIST].subChunks[index.TAG].value;
}
return null;
}
/**
* Write a RIFF tag in the INFO chunk. If the tag do not exist,
* then it is created. It if exists, it is overwritten.
* @param {string} tag The tag name.
* @param {string} value The tag value.
* @throws {Error} If the tag name is not valid.
*/
setTag(tag, value) {
tag = fixRIFFTag_(tag);
/** @type {!Object} */
let index = this.getTagIndex_(tag);
if (index.TAG !== null) {
this.LIST[index.LIST].subChunks[index.TAG].chunkSize =
value.length + 1;
this.LIST[index.LIST].subChunks[index.TAG].value = value;
} else if (index.LIST !== null) {
this.LIST[index.LIST].subChunks.push({
chunkId: tag,
chunkSize: value.length + 1,
value: value});
} else {
this.LIST.push({
chunkId: 'LIST',
chunkSize: 8 + value.length + 1,
format: 'INFO',
subChunks: []});
this.LIST[this.LIST.length - 1].subChunks.push({
chunkId: tag,
chunkSize: value.length + 1,
value: value});
}
}
/**
* Remove a RIFF tag from the INFO chunk.
* @param {string} tag The tag name.
* @return {boolean} True if a tag was deleted.
*/
deleteTag(tag) {
/** @type {!Object} */
let index = this.getTagIndex_(tag);
if (index.TAG !== null) {
this.LIST[index.LIST].subChunks.splice(index.TAG, 1);
return true;
}
return false;
}
/**
* Return a Object<tag, value> with the RIFF tags in the file.
* @return {!Object<string, string>} The file tags.
*/
listTags() {
/** @type {?number} */
let index = this.getLISTIndex('INFO');
/** @type {!Object} */
let tags = {};
if (index !== null) {
for (let i = 0, len = this.LIST[index].subChunks.length; i < len; i++) {
tags[this.LIST[index].subChunks[i].chunkId] =
this.LIST[index].subChunks[i].value;
}
}
return tags;
}
/**
* Return the index of a list by its type.
* @param {string} listType The list type ('adtl', 'INFO')
* @return {?number}
* @protected
*/
getLISTIndex(listType) {
for (let i = 0, len = this.LIST.length; i < len; i++) {
if (this.LIST[i].format == listType) {
return i;
}
}
return null;
}
/**
* Return the index of a tag in a FILE chunk.
* @param {string} tag The tag name.
* @return {!Object<string, ?number>}
* Object.LIST is the INFO index in LIST
* Object.TAG is the tag index in the INFO
* @private
*/
getTagIndex_(tag) {
/** @type {!Object<string, ?number>} */
let index = {LIST: null, TAG: null};
for (let i = 0, len = this.LIST.length; i < len; i++) {
if (this.LIST[i].format == 'INFO') {
index.LIST = i;
for (let j=0, subLen = this.LIST[i].subChunks.length; j < subLen; j++) {
if (this.LIST[i].subChunks[j].chunkId == tag) {
index.TAG = j;
break;
}
}
break;
}
}
return index;
}
}
/**
* Fix a RIFF tag format if possible, throw an error otherwise.
* @param {string} tag The tag name.
* @return {string} The tag name in proper fourCC format.
* @private
*/
function fixRIFFTag_(tag) {
if (tag.constructor !== String) {
throw new Error('Invalid tag name.');
} else if (tag.length < 4) {
for (let i = 0, len = 4 - tag.length; i < len; i++) {
tag += ' ';
}
}
return tag;
}