Audio Midi API-如何从音频样本中删除音高变化?

时间:2015-09-29 20:17:20

标签: audio midi

我一直在搞乱midi教程并触发样本。但是我无法弄清楚如何去除代码中样本的随机音高变化。知道如何消除音高变化吗?

链接到教程:http://www.keithmcmillen.com/blog/making-music-in-the-browser-web-midi-api/

<html>
<head>
    <meta charset="UTF-8">
    <title>Web MIDI API</title>
    <style>
    *:before, *:after {
        -webkit-box-sizing: border-box;
        -moz-box-sizing: border-box;
        box-sizing: border-box;
    }
    h4 {
        margin: 0 0 5px 0;
    }
    p {
        margin: 0 0 10px 0;
    }
    #content, #device_info {
        max-width: 800px;
        margin: 0 auto;
        padding: 10px 0;
        font-family: sans-serif;
        font-size: 12px;
        line-height: 12px;
        letter-spacing: 1.5px;
    }
    #content, #key_data {
        margin-top: 0px;
        text-align: center;
    }
    #inputs, #outputs {
        display: inline-block;
        width: 49%;
        margin-top: 10px;
        vertical-align: top;
    }
    #outputs {
        text-align: right;
    }
    .info {
        padding: 20px;
        border-radius: 3px;
        box-shadow: inset 0 0 10px #ccc;
        background-color: rgba(233,233,233,0.25);
    }
    .small {
        border-bottom: 1px solid #ccc;
        margin-left: 10px;
    }
    p:not(.small){
        text-transform: uppercase;
        font-weight: 800;
    }
    .button {
        display: inline-block;
        width: 100px;
        height: 100px;
        margin: 10px;
        background-color: #00adef;
        border-radius: 10px;
        opacity: 1;
        cursor: pointer;
        border: 2px solid white;
        transition: all 0.2s;
    }
    .button.active {
        background-color: #9a6aad;
        opacity: 0.25;
        box-shadow: inset 0px 0px 30px orange;
        border: 2px solid rgba(100,100,100,0.3);
        animation: shake .2s ease-in-out;
    }
    @keyframes shake {
        0% {
            transform: translateX(0);
            transform: translateY(0);
            transform: scale(1,1);
        }
        20% {
            transform: translateX(-10px);
            transform: translateY(-100px);
            transform: scale(0.5,0.75);
        }
        40% {
            transform: translateX(10px);
            transform: translateY(0px);
            transform: scale(1.5,2);
        }
        60% {
            transform: translateX(-10px);
            transform: translateY(-50px);
            transform: scale(0.5,0.75);
        }
        80% {
            transform: translateX(10px);
            transform: translateY(50px);
            transform: scale(1.3,2);
        }
        100% {
            transform: translateX(0);
            transform: translateY(0);
        }
    }
    </style>
</head>
<body>
    <div id="content">
        <div class="button" data-key="q" data-sound="audio/Slice1.mp3"></div>
        <div class="button" data-key="w" data-sound="audio/dinky-snare.mp3"></div>
        <div class="button" data-key="e" data-sound="audio/dinky-hat-2.mp3"></div>
        <div class="button" data-key="r" data-sound="audio/dinky-cym.mp3"></div>
        <div class="button" data-key="t" data-sound="audio/dinky-cym-noise.mp3"></div>
    </div>
    <div id="device_info">
        <div id="key_data"></div>
        <div id="inputs"></div>
        <div id="outputs"></div>
    </div>

    <script>
    //http://stackoverflow.com/questions/23687635/how-to-stop-audio-in-an-iframe-using-web-audio-api-after-hiding-its-container-di
    (function(){
        var log = console.log.bind(console), keyData = document.getElementById('key_data'), 
                deviceInfoInputs = document.getElementById('inputs'), deviceInfoOutputs = document.getElementById('outputs'), midi;
        var AudioContext = AudioContext || webkitAudioContext; // for ios/safari
        var context = new AudioContext();
        var activeNotes = [];
        var btnBox = document.getElementById('content'), btn = document.getElementsByClassName('button');
        var data, cmd, channel, type, note, velocity;

        // request MIDI access
        if(navigator.requestMIDIAccess){
            navigator.requestMIDIAccess({sysex: false}).then(onMIDISuccess, onMIDIFailure);
        }
        else {
            alert("No MIDI support in your browser.");
        }

        // add event listeners
        document.addEventListener('keydown', keyController);
        document.addEventListener('keyup', keyController);
        for(var i = 0; i < btn.length; i++){
            btn[i].addEventListener('mousedown', clickPlayOn);
            btn[i].addEventListener('mouseup', clickPlayOff);   
        }
        // prepare audio files
        for(var i = 0; i < btn.length; i++){
            addAudioProperties(btn[i]);
        }

        var sampleMap = {
            key60: 1,
            key61: 2,
            key62: 3,
            key63: 4,
            key64: 5
        };
        // user interaction 
        function clickPlayOn(e){
            e.target.classList.add('active');
            e.target.play();
        }

        function clickPlayOff(e){
            e.target.classList.remove('active');
        }

        function keyController(e){
            if(e.type == "keydown"){
                switch(e.keyCode){
                    case 81:
                        btn[0].classList.add('active');
                        btn[0].play();
                        break;
                    case 87:
                        btn[1].classList.add('active');
                        btn[1].play();
                        break;
                    case 69:
                        btn[2].classList.add('active');
                        btn[2].play();
                        break;
                    case 82:
                        btn[3].classList.add('active');
                        btn[3].play();
                        break;
                    case 84:
                        btn[4].classList.add('active');
                        btn[4].play();
                        break;                  
                    default:
                        //console.log(e);
                }
            }
            else if(e.type == "keyup"){
                switch(e.keyCode){
                    case 81:
                        btn[0].classList.remove('active');
                        break;
                    case 87:
                        btn[1].classList.remove('active');
                        break;
                    case 69:
                        btn[2].classList.remove('active');
                        break;
                    case 82:
                        btn[3].classList.remove('active');
                        break;
                    case 84:
                        btn[4].classList.remove('active');
                        break;
                    default:
                        //console.log(e.keyCode);
                }
            }
        }

        // midi functions
        function onMIDISuccess(midiAccess){
            midi = midiAccess;
            var inputs = midi.inputs.values();
            // loop through all inputs
            for(var input = inputs.next(); input && !input.done; input = inputs.next()){
                // listen for midi messages
                input.value.onmidimessage = onMIDIMessage;

                listInputs(input);
            }
            // listen for connect/disconnect message
            midi.onstatechange = onStateChange;

            showMIDIPorts(midi);
        }

        function onMIDIMessage(event){
            data = event.data,
            cmd = data[0] >> 4,
            channel = data[0] & 0xf,
            type = data[0] & 0xf0, // channel agnostic message type. Thanks, Phil Burk.
            note = data[1],
            velocity = data[2];
            // with pressure and tilt off
            // note off: 128, cmd: 8 
            // note on: 144, cmd: 9
            // pressure / tilt on
            // pressure: 176, cmd 11: 
            // bend: 224, cmd: 14
            log('MIDI data', data);
            switch(type){
                case 144: // noteOn message 
                    noteOn(note, velocity);
                    break;
                case 128: // noteOff message 
                    noteOff(note, velocity);
                    break;
            }

            //log('data', data, 'cmd', cmd, 'channel', channel);
            logger(keyData, 'key data', data);
        }

        function onStateChange(event){
            showMIDIPorts(midi);
            var port = event.port, state = port.state, name = port.name, type = port.type;
            if(type == "input")
                log("name", name, "port", port, "state", state);

        }

        function listInputs(inputs){
            var input = inputs.value;
                log("Input port : [ type:'" + input.type + "' id: '" + input.id + 
                        "' manufacturer: '" + input.manufacturer + "' name: '" + input.name + 
                        "' version: '" + input.version + "']");
        }

        function noteOn(midiNote, velocity){
            player(midiNote, velocity);
        }

        function noteOff(midiNote, velocity){
            player(midiNote, velocity);
        }

        function player(note, velocity){
            var sample = sampleMap['key'+note];
            if(sample){
                if(type == (0x80 & 0xf0) || velocity == 0){ //needs to be fixed for QuNexus, which always returns 144
                    btn[sample - 1].classList.remove('active');
                    return;
                }
                btn[sample - 1].classList.add('active');
                btn[sample - 1].play(velocity);
            }
        }

        function onMIDIFailure(e){
            log("No access to MIDI devices or your browser doesn't support WebMIDI API. Please use WebMIDIAPIShim " + e);
        }

        // MIDI utility functions
        function showMIDIPorts(midiAccess){
            var inputs = midiAccess.inputs,
                    outputs = midiAccess.outputs, 
                    html;
            html = '<h4>MIDI Inputs:</h4><div class="info">';
            inputs.forEach(function(port){
                html += '<p>' + port.name + '<p>';
                html += '<p class="small">connection: ' + port.connection + '</p>';
                html += '<p class="small">state: ' + port.state + '</p>';
                html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
                if(port.version){
                    html += '<p class="small">version: ' + port.version + '</p>';
                }
            });
            deviceInfoInputs.innerHTML = html + '</div>';

            html = '<h4>MIDI Outputs:</h4><div class="info">';
            outputs.forEach(function(port){
                html += '<p>' + port.name + '<br>';
                html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
                if(port.version){
                    html += '<p class="small">version: ' + port.version + '</p>';
                }
            });
            deviceInfoOutputs.innerHTML = html + '</div>';
        }

        // audio functions
        function loadAudio(object, url){
            var request = new XMLHttpRequest();
            request.open('GET', url, true);
            request.responseType = 'arraybuffer';
            request.onload = function(){
                context.decodeAudioData(request.response, function(buffer){
                    object.buffer = buffer;
                });
            }
            request.send();
        }

        function addAudioProperties(object){
            object.name = object.id;
            object.source = object.dataset.sound;
            loadAudio(object, object.source);
            object.play = function(volume){
                var s = context.createBufferSource();
                var g = context.createGain();
                var v;
                s.buffer = object.buffer;
                s.playbackRate.value = randomRange(0.5, 2);
                if(volume){
                    v = rangeMap(volume, 1, 127, 0.2, 2);
                    s.connect(g);
                    g.gain.value = v * v;
                    g.connect(context.destination);
                }
                else{
                    s.connect(context.destination); 
                }

                s.start();
                object.s = s;
            }
        }

        // utility functions
        function randomRange(min, max){
            return Math.random() * (max + min) + min;
        }

        function rangeMap(x, a1, a2, b1, b2){
            return ((x - a1)/(a2-a1)) * (b2 - b1) + b1;
        }

        //function frequencyFromNoteNumber( note ) {
        //  return 440 * Math.pow(2,(note-69)/12);
        //}

        function logger(container, label, data){
            messages = label + " [channel: " + (data[0] & 0xf) + ", cmd: " + (data[0] >> 4) + ", type: " + (data[0] & 0xf0) + " , note: " + data[1] + " , velocity: " + data[2] + "]";
            container.textContent = messages;
        }

    })();
    </script>
</body>
</html>

1 个答案:

答案 0 :(得分:0)

addAudioProperties()功能中有一行,每次都将播放速率设置为随机。您可以通过将播放速率设置为1来删除它:

s.playbackRate.value = randomRange(0.5, 2);

变为

s.playbackRate.value = 1;