1

Формат волнового файла поддерживает блок данных под названием "smpl", который распознается большинством плагинов DAW и большинства сэмплеров, таких как Kontakt. Блок данных позволяет вам указать начальную позицию цикла и конечную позицию цикла для волнового файла.

http://www.piclist.com/techref/io/serial/midi/wave.html https://web.archive.org/web/20141226210234/http://www.sonicspot.com/guide/wavefiles.html #smpl

Как я могу получить / установить свойства этого блока данных для нескольких файлов? В идеале я бы хотел сделать что-то вроде этого:

utility.exe input.wav output.wav --loopstart 44100 --loopend 88200

Где он принимает input.wav в качестве входа и выводит output.wav с начальными и конечными точками петли, вставленными в 1 секунду и 2 секунды (при условии звука 44,1 кГц) соответственно.

Я уже знаю о SoX, libsndfile и тому подобном, но, к сожалению, они не имеют такой функциональности. Есть ли другой способ сделать это?

1 ответ1

2

Я не уверен, что это решение в конечном итоге поможет кому-то, так как обстоятельства его настолько специфичны, но я все равно опубликую его на всякий случай.

Я не верю, что в Интернете существует утилита командной строки, которая может задавать точки волнового цикла, поэтому я решил написать в 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);
    }
}

Чтобы использовать функцию:

  1. Установить Node.js
  2. Сохраните вышеуказанную функцию в текстовом файле и переименуйте в tools.js
  3. Откройте окно командной строки Node.js
  4. require('C:/path/to/where/you/saved/tools.js')();
  5. process.chdir('C:/path/to/wave/files');
  6. SetLoop('input.wav','output.wav',1,2) // set loop start at 1.0 sec and loop end at 2.0 sec

Обратите внимание, что этот код полностью заменяет фрагмент файла smpl и как таковой отбрасывает любые существующие точки меток / петель в файле.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .