首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Tone.js实现弦频连续变化

用Tone.js实现弦频连续变化
EN

Stack Overflow用户
提问于 2021-04-01 06:46:19
回答 1查看 35关注 0票数 0

如何用tone.js实现吉他和弦基频的连续交互改变?

显然,将Tone.Sampler与可能命名为"pitchBend“或"pitchShift”的接口一起使用是一种可行的方法,但我不确定实际的选择。换言之,如何制作“投诉警察课滑块”?

我为一组振荡器(每个振荡器对应一个吉他弦)的本机window.AudioContext解决了这个问题,这些振荡器使用滑块中的频率进行更新,如下所示:

oscillator.frequency.setValueAtTime(频率,context.currentTime );

它很好地改变了所有弦的音调,而振荡器仍然活着并在演奏。

但1)音色不完全是吉他的默认正弦声音波形和2)弦有时会干扰或混响,使声音太不自然。

也许tone.js平台之外的其他平台可以做到这一点?

谢谢。

EN

回答 1

Stack Overflow用户

发布于 2021-04-04 07:47:34

我做了更多的研究,现在看起来原生WebAudio应用程序接口解决了这个问题,但还不清楚有多可靠和精确。我确实加载了吉他弦乐样本,并使滑块改变他们的速度比例来重新调整。代码如下:

代码语言:javascript
复制
    <!DOCTYPE html>
    <html>
      <head>
        <title>Continuous chord. Capo effect.</title>
        <meta charset="utf-8">
        <meta name="viewport"
              content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <script>
    
            window.onload = function () {
    
    
                //==================================================
                // //\\ configures sound parameters
                //==================================================
    
                // //\\ scale constants
                /*
                var logStep         = Math.log( 2 ) / 12;
                var semitone        = Math.exp( logStep );
                console.log( semitone );
                var semi2           = semitone * semitone;
                var semi4           = semi2 * semi2;
                var semi7           = semi2 * semitone;
                */
                // \\// scale constants
    
    
                //E3, A3, C3#,
                //165, 220, 277
                var initSpeed       = 1;
                var clipStart       = 0; //0.65; //0.7;
                var clipLenOriginal = 3.2; //0.43;
                var doLoop          = false; //true;
    
    
    
                //speed raise
                //if it is expected only one url for all tones in chord,
                //adjust here speeds to generate different tones according to
                //frequency ratios,
                var clipLenSpeeds   = [
                    //minor
                    /*
                    var smR0  = 1;
                    var smR1  = 262/220;
                    var smR2  = 330/220;
                    */
                    //major
                    1,                      
                    1, // 277/220, //220/165
                    1, // 330/220, //277/165
                ];
                var clipLengs       = clipLenSpeeds.map( sp => clipLenOriginal*sp );
                var clipLenSpeeds   = clipLenSpeeds.map( sp => initSpeed*sp );
    
                var dataPath        = 'data';
                //data are from this path:
                //var dataPath      = 'https://github.com/nbrosowsky/tonejs-instruments/tree/master/samples/guitar-acoustic';
                var urls            = [
                    dataPath + '/' + 'A3.wav',
                    dataPath + '/' + 'E3.wav',
                    dataPath + '/' + 'Cs3.wav',
                ];
                //var fname = 'second-line-captured.mkv.mp3';
                //var urls            = clipLenSpeeds.map( el => fname );
                //==================================================
                // \\// configures sound parameters
                //==================================================
    
    
                //==================================================
                // //\\ loads and prepares sourceNodes
                //==================================================
                let audioContext    = new AudioContext();
                var sourceNodes     = [];
                var audioBuffers    = [];
    
                Promise.all(
                    clipLenSpeeds.map( ( el, ix ) => 
                        getSample( ix )
                    )
                ).then(
                    ( values ) => {
                        //console.log( 'values from last promise-then:', values );
                        button.removeAttribute( 'disabled' );
                    },
                ).catch( (err) => {
                    console.log( err );
                });
                //==================================================
                // \\// loads and prepares sourceNodes
                //==================================================
    
    
    
                //==================================================
                // //\\ GUI
                //==================================================
                const button            = document.querySelector( '.run' );
                const playbackControl   = document.querySelector( '.playback-rate-control' );
                const playbackValue     = document.querySelector( '.playback-rate-value' );
                button.setAttribute( 'disabled', 'disabled' );
                button.addEventListener( 'click', () => {
                    audioBuffers.forEach( (val,vix) => 
                        startLoopByIx( vix )
                    );
                });
                playbackControl.oninput = function() {
                    playbackValue.innerHTML = playbackControl.value;
                    sourceNodes.forEach( (sn,i) => {
                        sn.playbackRate.value = clipLenSpeeds[i] * playbackControl.value;
                    });
                }
                //==================================================
                // \\// GUI
                //==================================================
                return;
    
    
    
    
    
    
                ///part of the load and initation of audio buffer,
                ///returns promise from the last ".then"
                function getSample( ix )
                {
                    return fetch( urls[ ix ] )
                        .then( response => response.arrayBuffer() )
                        .then( arrayBuffer => audioContext.decodeAudioData( arrayBuffer ) )
                        .then( audioBuffer => {
                            audioBuffers[ ix ] = audioBuffer;
    
                            //if uncommented, this returned value populates
                            //values in Promise.all.then first param,
                            //return audioBuffer;
                        })
                        ;
                }
    
                ///fires up loop belonging to sourceNode[ ix ],
                ///options: either doLoop or one-time play,
                function startLoopByIx( ix )
                {
                    var audioBuffer = audioBuffers[ ix ];
                    var clipLen     = clipLengs[ ix ];
                    var rate        = clipLenSpeeds[ ix ];
                    var pan         = 0; //stereo channels balance
    
                    let sourceNode = audioContext.createBufferSource();
                    let pannerNode = audioContext.createStereoPanner();
    
                    sourceNode.buffer               = audioBuffer;
                    sourceNode.loop                 = doLoop;
                    sourceNode.loopStart            = clipStart;
                    sourceNode.loopEnd              = clipStart + clipLen;
                    sourceNode.playbackRate.value   = rate; //does retuning
                    pannerNode.pan.value            = pan;
    
                    sourceNode.connect( pannerNode );
                    pannerNode.connect( audioContext.destination );
    
                    if( doLoop ) {
                        sourceNode.start( 0, clipStart, );
                    } else {
                        sourceNode.start( 0, clipStart, clipLen );
                    }
                    sourceNodes[ ix ] = sourceNode;
                }
            };
        </script>
      </head>
      <body>
    
        <button class="run">Run samples</button><br>
    
        <h2>Set playback rate</h2>
        <input class="playback-rate-control" type="range" min="0.25" max="3" step="0.05" value="1">
        <span class="playback-rate-value">1.0</span>
    
    
      </body>
    </html>
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66896251

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档