Я не уверен, что это решение в конечном итоге поможет кому-то, так как обстоятельства его настолько специфичны, но я все равно опубликую его на всякий случай.
Я не верю, что в Интернете существует утилита командной строки, которая может задавать точки волнового цикла, поэтому я решил написать в Node.js функцию для достижения этой цели.
module.exports = function()
{
this.SetLoop = function(inputPath, outputPath, start, end)
{
// load input wave
var waveData = require('fs').readFileSync(inputPath);
// get sample rate of wave file
var sampleRateIndex;
for(var i = 0; i < waveData.length-4; i++)
{
if (waveData.toString('utf8', i, i + 4) === 'fmt ') // look for fmt chunk - which contains sample rate among other things - per wave format specification
{
sampleRateIndex = i + 4 + 4 + 2 + 2; // sample rate is 12 bytes after start of 'fmt ' chunk id
break;
}
}
if (sampleRateIndex === undefined)
return;
var sampleRate = waveData.readUInt32LE(sampleRateIndex);
// convert seconds to samples
start = Math.floor(sampleRate*start);
end = Math.floor(sampleRate*end);
// find index (byte offset) of smpl chunk if it exists
var smplIndex;
for(var i = waveData.length-4-1; i >= 0; i--) // start search from end of file going backward, since the smpl chunk is typically written after the actual waveform data
{
if (waveData.toString('utf8', i, i + 4) === 'smpl')
{
smplIndex = i; // start of smpl chunk id
break;
}
}
// if the smpl chunk already exists, remove it
if (smplIndex !== undefined)
{
var smplChunkSize = waveData.readUInt32LE(smplIndex + 4) + 8; // smpl chunk size is specified 4 bytes after start of smpl chunk id. add 8 bytes to include size of smpl chunk header itself
waveData = Buffer.concat( [ waveData.slice(0, smplIndex) , waveData.slice(smplIndex + smplChunkSize) ] ); // splice smpl chunk from wave file data
}
// make new buffer to replace smpl chunk
var smplChunk = Buffer.alloc(68); // the default smpl chunk written here is 60 bytes long. add 8 bytes to include size of smpl chunk header itself
// all bytes other than the ones specified below default to 0 and represent default values for the smpl chunk properties
smplChunk.write('smpl', 0, 4);
smplChunk.writeUInt32LE(60, 4); // the default smpl chunk written here is 60 bytes long
smplChunk.writeUInt32LE(60, 20); // middle C is MIDI note 60, therefore make MIDI unity note 60
smplChunk.writeUInt32LE(1, 36); // write at byte offset 36 that there is one loop cue info in the file
smplChunk.writeUInt32LE(start, 52); // write loop start point at byte offset 52
smplChunk.writeUInt32LE(end, 56); // write loop end point at byte offset 56
// append new smpl chunk to wave file
waveData = Buffer.concat( [ waveData, smplChunk ] );
// change wave file main header data to increase the file size to include smpl chunk (loop points)
var fileSizeIndex;
for(var i = 0; i < waveData.length-4; i++)
{
if (waveData.toString('utf8', i, i + 4) === 'RIFF') // look for RIFF chunk (should always be at the very beginning of file)
{
fileSizeIndex = i + 4; // file size is 4 bytes after start of RIFF chunk id
break;
}
}
if (fileSizeIndex === undefined)
return;
var fileSize = waveData.length-8; // get final length of wave file, minus 8 bytes to not include the RIFF chunk header itself
waveData.writeUInt32LE(fileSize, fileSizeIndex); // write new file length
// write new wave file
require('fs').writeFileSync(outputPath, waveData);
}
}
Чтобы использовать функцию:
- Установить Node.js
- Сохраните вышеуказанную функцию в текстовом файле и переименуйте в tools.js
- Откройте окно командной строки Node.js
require('C:/path/to/where/you/saved/tools.js')();
process.chdir('C:/path/to/wave/files');
SetLoop('input.wav','output.wav',1,2) // set loop start at 1.0 sec and loop end at 2.0 sec
Обратите внимание, что этот код полностью заменяет фрагмент файла smpl и как таковой отбрасывает любые существующие точки меток / петель в файле.