({ streams, audioNorm, outputVolume })
| 157 | } |
| 158 | |
| 159 | async function mixArbitraryAudio({ streams, audioNorm, outputVolume }) { |
| 160 | let maxGain = 30; |
| 161 | let gaussSize = 5; |
| 162 | if (audioNorm) { |
| 163 | if (audioNorm.gaussSize != null) gaussSize = audioNorm.gaussSize; |
| 164 | if (audioNorm.maxGain != null) maxGain = audioNorm.maxGain; |
| 165 | } |
| 166 | const enableAudioNorm = audioNorm && audioNorm.enable; |
| 167 | |
| 168 | // https://stackoverflow.com/questions/35509147/ffmpeg-amix-filter-volume-issue-with-inputs-of-different-duration |
| 169 | let filterComplex = streams.map(({ start, cutFrom, cutTo }, i) => { |
| 170 | const cutToArg = (cutTo != null ? `:end=${cutTo}` : ''); |
| 171 | const apadArg = i > 0 ? ',apad' : ''; // Don't pad the first track (audio from video clips with correct duration) |
| 172 | |
| 173 | return `[${i}]atrim=start=${cutFrom || 0}${cutToArg},adelay=delays=${Math.floor((start || 0) * 1000)}:all=1${apadArg}[a${i}]`; |
| 174 | }).join(';'); |
| 175 | |
| 176 | const volumeArg = outputVolume != null ? `,volume=${outputVolume}` : ''; |
| 177 | const audioNormArg = enableAudioNorm ? `,dynaudnorm=g=${gaussSize}:maxgain=${maxGain}` : ''; |
| 178 | filterComplex += `;${streams.map((s, i) => `[a${i}]`).join('')}amix=inputs=${streams.length}:duration=first:dropout_transition=0:weights=${streams.map((s) => (s.mixVolume != null ? s.mixVolume : 1)).join(' ')}${audioNormArg}${volumeArg}`; |
| 179 | |
| 180 | const mixedAudioPath = join(tmpDir, 'audio-mixed.flac'); |
| 181 | |
| 182 | const args = [ |
| 183 | ...getFfmpegCommonArgs({ enableFfmpegLog }), |
| 184 | ...(flatMap(streams, ({ path, loop }) => ([ |
| 185 | '-stream_loop', (loop || 0), |
| 186 | '-i', path, |
| 187 | ]))), |
| 188 | '-filter_complex', filterComplex, |
| 189 | '-c:a', 'flac', |
| 190 | '-y', |
| 191 | mixedAudioPath, |
| 192 | ]; |
| 193 | |
| 194 | if (verbose) console.log(args.join(' ')); |
| 195 | |
| 196 | await execa(ffmpegPath, args); |
| 197 | |
| 198 | return mixedAudioPath; |
| 199 | } |
| 200 | |
| 201 | async function editAudio({ keepSourceAudio, clips, arbitraryAudio, clipsAudioVolume, audioNorm, outputVolume }) { |
| 202 | // We need clips to process audio, because we need to know duration |
no test coverage detected