MIDI Eğitimi: MIDI Donanımı Tarafından Kontrol Edilen Tarayıcı Tabanlı Ses Uygulamaları Oluşturma

Yayınlanan: 2022-03-11

Web Ses API'sinin popülaritesi, özellikle HTML5 oyun geliştiricileri arasında artarken, Web MIDI API'si, ön uç geliştiriciler arasında hala çok az biliniyor. Bunun büyük bir kısmı muhtemelen mevcut destek eksikliği ve erişilebilir belgelerle ilgilidir; Web MIDI API'si şu anda yalnızca Google Chrome'da desteklenmektedir, bunun için özel bir bayrak etkinleştirmeniz sağlanır. ES7 standardının bir parçası olması planlandığı için tarayıcı üreticileri şu anda bu API'ye çok az önem veriyor.

80'lerin başında çeşitli müzik endüstrisi temsilcileri tarafından tasarlanan MIDI (Müzik Aleti Dijital Arayüzü'nün kısaltması), elektronik müzik cihazları için standart bir iletişim protokolüdür. OSC gibi diğer protokoller o zamandan beri geliştirilmiş olsa da; otuz yıl sonra, MIDI hala ses donanımı üreticileri için fiili iletişim protokolüdür. Stüdyosunda en az bir MIDI cihazı olmayan modern bir müzik yapımcısı bulmakta zorlanacaksınız.

Web Audio API'nin hızla geliştirilmesi ve benimsenmesiyle, artık bulut ve fiziksel dünya arasındaki boşluğu dolduran tarayıcı tabanlı uygulamalar oluşturmaya başlayabiliriz. Web MIDI API, sentezleyiciler ve ses efektleri oluşturmamıza izin vermekle kalmaz, aynı zamanda özellik ve performans açısından mevcut flash tabanlı benzerlerine benzer tarayıcı tabanlı DAW (Dijital Ses İş İstasyonu) oluşturmaya bile başlayabiliriz (örneğin Audiotool'a bakın). ).

Bu MIDI eğitiminde, Web MIDI API'sinin temelleri konusunda size rehberlik edeceğim ve en sevdiğiniz MIDI cihazınızla oynayabileceğiniz basit bir monosynth oluşturacağız. Tam kaynak kodu burada mevcuttur ve canlı demoyu doğrudan test edebilirsiniz. Bir MIDI aygıtınız yoksa, GitHub deposunun 'klavye' dalını kontrol ederek bu öğreticiyi takip edebilirsiniz, bu da bilgisayar klavyeniz için temel destek sağlar, böylece notları çalabilir ve oktavları değiştirebilirsiniz. Bu aynı zamanda canlı demo olarak mevcut olan versiyondur. Bununla birlikte, bilgisayar donanımının sınırlamaları nedeniyle, sentezleyiciyi kontrol etmek için bilgisayar klavyenizi her kullandığınızda, hız ve detune devre dışı bırakılır. Anahtar/not eşleme hakkında bilgi almak için lütfen GitHub'daki benioku dosyasına bakın.

Toptal'ın midi eğitimi

Midi Eğitimi Ön Koşulları

Bu MIDI öğreticisi için aşağıdakilere ihtiyacınız olacak:

  • #enable-web-midi bayrağı etkinleştirilmiş Google Chrome (sürüm 38 veya üzeri)
  • (Opsiyonel) Bilgisayarınıza bağlı notaları tetikleyebilen bir MIDI cihazı

Uygulamamıza biraz yapı getirmek için Angular.js'yi de kullanacağız; bu nedenle, çerçevenin temel bilgisi bir ön koşuldur.

Başlarken

MIDI uygulamamızı 3 modüle ayırarak sıfırdan modüler hale getireceğiz:

  • WebMIDI: bilgisayarınıza bağlı çeşitli MIDI aygıtlarını yönetme
  • WebAudio: synth'imiz için ses kaynağı sağlamak
  • WebSynth: web arayüzünü ses motoruna bağlama

Bir App modülü, web kullanıcı arayüzü ile kullanıcı etkileşimini yönetecektir. Uygulama yapımız biraz şöyle görünebilir:

 |- app |-- js |--- midi.js |--- audio.js |--- synth.js |--- app.js |- index.html

Uygulamanızı oluşturmanıza yardımcı olması için şu kitaplıkları da kurmalısınız: Angular.js, Bootstrap ve jQuery. Bunları kurmanın muhtemelen en kolay yolu Bower'dır.

WebMIDI Modülü: Gerçek Dünya ile Bağlantı

MIDI cihazlarımızı uygulamamıza bağlayarak MIDI'yi nasıl kullanacağımızı anlamaya başlayalım. Bunu yapmak için, tek bir yöntem döndüren basit bir fabrika oluşturacağız. MIDI cihazlarımıza Web MIDI API aracılığıyla bağlanmak için navigator.requestMIDIAccess yöntemini çağırmamız gerekiyor:

 angular .module('WebMIDI', []) .factory('Devices', ['$window', function($window) { function _connect() { if($window.navigator && 'function' === typeof $window.navigator.requestMIDIAccess) { $window.navigator.requestMIDIAccess(); } else { throw 'No Web MIDI support'; } } return { connect: _connect }; }]);

Ve bu kadar!

requestMIDIAccess yöntemi bir söz verir, bu nedenle doğrudan geri döndürebilir ve sözümüzün sonucunu uygulamamızın denetleyicisinde işleyebiliriz:

 angular .module('DemoApp', ['WebMIDI']) .controller('AppCtrl', ['$scope', 'Devices', function($scope, devices) { $scope.devices = []; devices .connect() .then(function(access) { if('function' === typeof access.inputs) { // deprecated $scope.devices = access.inputs(); console.error('Update your Chrome version!'); } else { if(access.inputs && access.inputs.size > 0) { var inputs = access.inputs.values(), input = null; // iterate through the devices for (input = inputs.next(); input && !input.done; input = inputs.next()) { $scope.devices.push(input.value); } } else { console.error('No devices detected!'); } } }) .catch(function(e) { console.error(e); }); }]);

Belirtildiği gibi, requestMIDIAccess yöntemi, iki özelliğe sahip bir nesneyi then yöntemine ileterek bir söz verir: girdiler ve çıktılar.

Chrome'un önceki sürümlerinde, bu iki özellik, bir dizi giriş ve çıkış aygıtını doğrudan almanıza olanak tanıyan yöntemlerdi. Ancak en son güncellemelerde bu özellikler artık nesnelerdir. Bu oldukça fark yaratır, çünkü artık ilgili aygıt listesini almak için girişler veya çıkışlar nesnesindeki values yöntemini çağırmamız gerekir. Bu yöntem bir üreteç işlevi görür ve bir yineleyici döndürür. Yine, bu API'nin ES7'nin bir parçası olması amaçlanmıştır; bu nedenle, orijinal uygulama kadar basit olmasa da, oluşturucu benzeri davranışı uygulamak mantıklıdır.

Son olarak, yineleyici nesnesinin size özelliği aracılığıyla cihaz sayısını alabiliriz. En az bir cihaz varsa, yineleyici nesnenin next yöntemini çağırarak ve her cihazı $scope'ta tanımlanan bir diziye iterek sonuç üzerinde yineleniriz. Ön uçta, mevcut tüm giriş cihazlarını listeleyecek ve web synth'i kontrol etmek için hangi cihazı aktif cihaz olarak kullanmak istediğimizi seçmemize izin verecek basit bir seçim kutusu uygulayabiliriz:

 <select ng-model="activeDevice" class="form-control" ng-options="device.manufacturer + ' ' + device.name for device in devices"> <option value="" disabled>Choose a MIDI device...</option> </select>

Bu seçim kutusunu, daha sonra bu aktif cihazı synth'e bağlamak için kullanacağımız activeDevice adlı bir $scope değişkenine bağladık.

bu aktif cihazı synth'e bağlayın

WebAudio Modülü: Gürültü Çıkarma

WebAudio API, yalnızca ses dosyalarını çalmamıza değil, aynı zamanda diğerlerinin yanı sıra osilatörler, filtreler ve kazanç düğümleri gibi sentezleyicilerin temel bileşenlerini yeniden oluşturarak sesler üretmemize olanak tanır.

Bir Osilatör Oluşturun

Osilatörlerin rolü, bir dalga formu vermektir. WebAudio API'de dördü desteklenen çeşitli dalga biçimleri vardır: sinüs, kare, üçgen ve testere dişi. Dalga formlarının belirli bir frekansta "salındığı" söylenir, ancak gerekirse kendi özel dalga tablosunu tanımlaması da mümkündür. Belirli bir frekans aralığı insanlar tarafından duyulabilir - bunlar ses olarak bilinir. Alternatif olarak, düşük frekanslarda salındıklarında osilatörler, seslerimizi modüle edebilmemiz için LFO'lar (“düşük frekanslı osilatör”) oluşturmamıza da yardımcı olabilir (ancak bu, bu eğitimin kapsamı dışındadır).

Biraz ses oluşturmak için yapmamız gereken ilk şey, yeni bir AudioContext örneği oluşturmaktır:

 function _createContext() { self.ctx = new $window.AudioContext(); }

Oradan, WebAudio API tarafından sağlanan bileşenlerden herhangi birini somutlaştırabiliriz. Her bileşenin birden çok örneğini oluşturabileceğimiz için, ihtiyacımız olan bileşenlerin yeni, benzersiz örneklerini oluşturabilmek için hizmetler oluşturmak mantıklıdır. Yeni bir osilatör oluşturmak için hizmeti oluşturarak başlayalım:

 angular .module('WebAudio', []) .service('OSC', function() { var self; function Oscillator(ctx) { self = this; self.osc = ctx.createOscillator(); return self; } });

Artık, daha önce oluşturduğumuz AudioContext örneğini bir argüman olarak ileterek, yeni osilatörleri kendi isteğimizle başlatabiliriz. Yolda işleri kolaylaştırmak için bazı sarma yöntemleri ekleyeceğiz - yalnızca sözdizimsel şeker - ve Osilatör işlevini döndüreceğiz:

 Oscillator.prototype.setOscType = function(type) { if(type) { self.osc.type = type } } Oscillator.prototype.setFrequency = function(freq, time) { self.osc.frequency.setTargetAtTime(freq, 0, time); }; Oscillator.prototype.start = function(pos) { self.osc.start(pos); } Oscillator.prototype.stop = function(pos) { self.osc.stop(pos); } Oscillator.prototype.connect = function(i) { self.osc.connect(i); } Oscillator.prototype.cancel = function() { self.osc.frequency.cancelScheduledValues(0); } return Oscillator;

Çoklu Geçiş Filtresi ve Ses Kontrolü Oluşturun

Temel ses motorumuzu tamamlamak için iki bileşene daha ihtiyacımız var: sesimize biraz şekil vermek için bir çok geçişli filtre ve sesimizin yüksekliğini kontrol etmek ve sesi açıp kapatmak için bir kazanç düğümü. Bunu yapmak için, osilatör için yaptığımız gibi ilerleyebiliriz: bazı sarmalayıcı yöntemlerle bir işlev döndüren hizmetler oluşturun. Tek yapmamız gereken AudioContext örneğini sağlamak ve uygun yöntemi çağırmak.

AudioContext örneğinin createBiquadFilter yöntemini çağırarak bir filtre oluşturuyoruz:

 ctx.createBiquadFilter();

Benzer şekilde, bir kazanç düğümü için createGain yöntemini çağırırız:

 ctx.createGain();

WebSynth Modülü: Her Şeyi Kablolama

Artık synth arabirimimizi oluşturmaya ve MIDI cihazlarını ses kaynağımıza bağlamaya neredeyse hazırız. Öncelikle audio motorumuzu birbirine bağlamamız ve MIDI notları almaya hazır hale getirmemiz gerekiyor. Ses motorunu bağlamak için, ihtiyacımız olan bileşenlerin yeni örneklerini oluşturmamız ve ardından her bileşenin örnekleri için mevcut olan connect yöntemini kullanarak bunları birbirine “bağlamamız” yeterlidir. connect yöntemi, yalnızca geçerli örneği bağlamak istediğiniz bileşen olan bir argüman alır. Connect yöntemi bir düğümü birden çok modülatöre connect (çapraz sönümleme ve daha fazlası gibi şeyleri uygulamayı mümkün kılar) daha ayrıntılı bir bileşen zincirini düzenlemek mümkündür.

 self.osc1 = new Oscillator(self.ctx); self.osc1.setOscType('sine'); self.amp = new Amp(self.ctx); self.osc1.connect(self.amp.gain); self.amp.connect(self.ctx.destination); self.amp.setVolume(0.0, 0); //mute the sound self.filter1.disconnect(); self.amp.disconnect(); self.amp.connect(self.ctx.destination); }

Ses motorumuzun dahili kablolarını yeni kurduk. Biraz oynayabilir ve farklı kablo kombinasyonlarını deneyebilirsiniz, ancak sağır olmamak için sesi kısmayı unutmayın. Artık MIDI arayüzünü uygulamamıza bağlayabilir ve ses motoruna MIDI mesajları gönderebiliriz. Synth'imize sanal olarak "takmak" için cihaz seçim kutusuna bir izleyici kuracağız. Ardından cihazdan gelen MIDI mesajlarını dinleyeceğiz ve bilgileri ses motoruna ileteceğiz:

 // in the app's controller $scope.$watch('activeDevice', DSP.plug); // in the synth module function _onmidimessage(e) { /** * e.data is an array * e.data[0] = on (144) / off (128) / detune (224) * e.data[1] = midi note * e.data[2] = velocity || detune */ switch(e.data[0]) { case 144: Engine.noteOn(e.data[1], e.data[2]); break; case 128: Engine.noteOff(e.data[1]); break; } } function _plug(device) { self.device = device; self.device.onmidimessage = _onmidimessage; }

Burada cihazdan MIDI olaylarını dinliyor, MidiEvent Object'ten gelen verileri analiz ediyor ve uygun yönteme aktarıyoruz; olay koduna bağlı olarak noteOn veya noteOff (noteOn için 144, noteOff için 128). Artık gerçekten bir ses üretmek için ses modülündeki ilgili yöntemlere mantığı ekleyebiliriz:

 function _noteOn(note, velocity) { self.activeNotes.push(note); self.osc1.cancel(); self.currentFreq = _mtof(note); self.osc1.setFrequency(self.currentFreq, self.settings.portamento); self.amp.cancel(); self.amp.setVolume(1.0, self.settings.attack); } function _noteOff(note) { var position = self.activeNotes.indexOf(note); if (position !== -1) { self.activeNotes.splice(position, 1); } if (self.activeNotes.length === 0) { // shut off the envelope self.amp.cancel(); self.currentFreq = null; self.amp.setVolume(0.0, self.settings.release); } else { // in case another note is pressed, we set that one as the new active note self.osc1.cancel(); self.currentFreq = _mtof(self.activeNotes[self.activeNotes.length - 1]); self.osc1.setFrequency(self.currentFreq, self.settings.portamento); } }

Burada birkaç şey oluyor. noteOn yönteminde önce mevcut notayı bir dizi nota itiyoruz. Bir monosynth oluşturuyor olsak da (yani bir seferde yalnızca bir nota çalabiliyoruz), klavyede aynı anda birkaç parmağımız olabilir. Bu yüzden, bir nota bıraktığımızda bir sonrakinin çalınması için tüm tez notlarını sıraya koymamız gerekiyor. Daha sonra, bir MIDI notundan (0'dan 127'ye kadar ölçek) dönüştürdüğümüz yeni frekansı, biraz matematikle gerçek bir frekans değerine atamak için osilatörü durdurmamız gerekir:

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

noteOff yönteminde ilk olarak aktif notlar dizisindeki notayı bulup kaldırarak başlıyoruz. Ardından, dizideki tek notsa, sesi kapatmamız yeterlidir.

setVolume yönteminin ikinci argümanı, kazancın yeni hacim değerine ulaşmasının ne kadar sürdüğünü ifade eden geçiş süresidir. Müzikal anlamda, nota açıksa atak zamanına, nota kapalıysa çıkış zamanına eşdeğer olacaktır.

WebAnalyser Modülü: Sesimizi Görselleştirme

Synth'imize ekleyebileceğimiz bir başka ilginç özellik de, sesimizin dalga biçimini tuval kullanarak görüntülememizi sağlayan bir analizör düğümüdür. Bir çözümleyici düğümü oluşturmak, diğer AudioContext nesnelerinden biraz daha karmaşıktır, çünkü analizi gerçekten gerçekleştirmek için bir komut dosyasıİşlemci düğümü de oluşturmayı gerektirir. DOM'da canvas öğesini seçerek başlıyoruz:

 function Analyser(canvas) { self = this; self.canvas = angular.element(canvas) || null; self.view = self.canvas[0].getContext('2d') || null; self.javascriptNode = null; self.analyser = null; return self; }

Ardından, hem analizörü hem de komut dosyası işlemcisini oluşturacağımız bir connect yöntemi ekliyoruz:

 Analyser.prototype.connect = function(ctx, output) { // setup a javascript node self.javascriptNode = ctx.createScriptProcessor(2048, 1, 1); // connect to destination, else it isn't called self.javascriptNode.connect(ctx.destination); // setup an analyzer self.analyser = ctx.createAnalyser(); self.analyser.smoothingTimeConstant = 0.3; self.analyser.fftSize = 512; // connect the output to the destination for sound output.connect(ctx.destination); // connect the output to the analyser for processing output.connect(self.analyser); self.analyser.connect(self.javascriptNode); // define the colors for the graph var gradient = self.view.createLinearGradient(0, 0, 0, 200); gradient.addColorStop(1, '#000000'); gradient.addColorStop(0.75, '#ff0000'); gradient.addColorStop(0.25, '#ffff00'); gradient.addColorStop(0, '#ffffff'); // when the audio process event is fired on the script processor // we get the frequency data into an array // and pass it to the drawSpectrum method to render it in the canvas self.javascriptNode.onaudioprocess = function() { // get the average for the first channel var array = new Uint8Array(self.analyser.frequencyBinCount); self.analyser.getByteFrequencyData(array); // clear the current state self.view.clearRect(0, 0, 1000, 325); // set the fill style self.view.fillStyle = gradient; drawSpectrum(array); } };

İlk olarak bir scriptProcessor nesnesi oluşturuyoruz ve onu hedefe bağlıyoruz. Ardından osilatör veya filtreden gelen ses çıkışı ile beslediğimiz analizörün kendisini oluşturuyoruz. Duyabilmemiz için ses çıkışını hedefe bağlamamız gerektiğine dikkat edin! Grafiğimizin degrade renklerini de tanımlamamız gerekiyor - bu, canvas öğesinin createLinearGradient yöntemini çağırarak yapılır.

Son olarak, scriptProcessor belirli aralıklarla bir 'audioprocess' olayı başlatır; bu olay tetiklendiğinde, analiz cihazı tarafından yakalanan ortalama frekansları hesaplar, tuvali temizler ve drawSpectrum yöntemini çağırarak yeni frekans grafiğini yeniden çizeriz:

 function drawSpectrum(array) { for (var i = 0; i < (array.length); i++) { var v = array[i], h = self.canvas.height(); self.view.fillRect(i * 2, h - (v - (h / 4)), 1, v + (h / 4)); } }

Son olarak, bu yeni bileşene uyum sağlamak için ses motorumuzun kablolarını biraz değiştirmemiz gerekecek:

 // in the _connectFilter() method if(self.analyser) { self.analyser.connect(self.ctx, self.filter1); } else { self.filter1.connect(self.ctx.destination); } // in the _disconnectFilter() method if(self.analyser) { self.analyser.connect(self.ctx, self.amp); } else { self.amp.connect(self.ctx.destination); }

Artık synth'imizin dalga biçimini gerçek zamanlı olarak görüntülememize izin veren güzel bir görselleştiricimiz var! Bu, kurulum için biraz çalışma gerektirir, ancak özellikle filtreleri kullanırken çok ilginç ve anlayışlıdır.

Synth'imizi Geliştirmek: Hız ve Detune Eklemek

MIDI eğitimimizin bu noktasında oldukça havalı bir synth'imiz var - ancak her notayı aynı ses seviyesinde çalıyor. Bunun nedeni, hız verilerini düzgün bir şekilde işlemek yerine, hacmi sabit bir 1.0 değerine ayarlamamızdır. Bunu düzelterek başlayalım ve sonra en yaygın MIDI klavyelerinde bulduğunuz detune tekerleğini nasıl etkinleştirebileceğimizi göreceğiz.

Hızı Etkinleştirme

Eğer aşina değilseniz, 'hız' klavyenizdeki tuşa ne kadar sert bastığınızla ilgilidir. Bu değere göre oluşturulan ses ya daha yumuşak ya da daha yüksek görünür.

MIDI öğretici synth'imizde, kazanç düğümünün hacmiyle oynayarak bu davranışı taklit edebiliriz. Bunu yapmak için, önce MIDI verilerini kazanç düğümüne geçmek üzere 0.0 ile 1.0 arasında bir kayan değere dönüştürmek için biraz matematik yapmamız gerekiyor:

 function _vtov (velocity) { return (velocity / 127).toFixed(2); }

Bir MIDI cihazının hız aralığı 0 ile 127 arasındadır, bu yüzden bu değeri 127'ye böleriz ve iki ondalık basamaklı bir kayan nokta değeri döndürürüz. Ardından, hesaplanan değeri kazanç düğümüne iletmek için _noteOn yöntemini güncelleyebiliriz:

 self.amp.setVolume(_vtov(velocity), self.settings.attack);

Ve bu kadar! Şimdi, synth'imizi çaldığımızda, klavyemizdeki tuşlara ne kadar sert bastığımıza bağlı olarak seslerin değiştiğini fark edeceğiz.

MIDI Klavyenizde Detune Wheel'i Etkinleştirme

Çoğu MIDI klavyede bir ayar çarkı bulunur; tekerlek, çalınmakta olan notanın frekansını hafifçe değiştirmenize olanak vererek, 'detune' olarak bilinen ilginç bir efekt yaratır. MIDI'yi nasıl kullanacağınızı öğrenirken bunu uygulamak oldukça kolaydır, çünkü ayar çarkı aynı zamanda kendi olay koduyla (224) bir MidiMessage olayını ateşler; bu olay, frekans değerini yeniden hesaplayarak ve osilatörü güncelleyerek dinleyebilir ve buna göre hareket edebiliriz.

Öncelikle synth'imizde olayı yakalamamız gerekiyor. Bunu yapmak için, _onmidimessage geri aramasında oluşturduğumuz switch ifadesine fazladan bir durum ekliyoruz:

 case 224: // the detune value is the third argument of the MidiEvent.data array Engine.detune(e.data[2]); break;

Ardından, ses motorunda detune yöntemini tanımlarız:

 function _detune(d) { if(self.currentFreq) { //64 = no detune if(64 === d) { self.osc1.setFrequency(self.currentFreq, self.settings.portamento); self.detuneAmount = 0; } else { var detuneFreq = Math.pow(2, 1 / 12) * (d - 64); self.osc1.setFrequency(self.currentFreq + detuneFreq, self.settings.portamento); self.detuneAmount = detuneFreq; } } }

Varsayılan detune değeri 64'tür, bu, uygulanan herhangi bir detune olmadığı anlamına gelir, bu nedenle bu durumda mevcut frekansı osilatöre iletiyoruz.

Son olarak, başka bir notun kuyruğa alınması durumunda detune'u dikkate almak için _noteOff yöntemini de güncellememiz gerekiyor:

 self.osc1.setFrequency(self.currentFreq + self.detuneAmount, self.settings.portamento);

Arayüz Oluşturma

Şimdiye kadar sadece MIDI cihazımızı ve bir dalga formu görselleştiricisini seçebilmek için bir seçim kutusu oluşturduk, ancak sesi doğrudan web sayfası ile etkileşime girerek değiştirme imkanımız yok. Ortak form öğelerini kullanarak çok basit bir arayüz oluşturalım ve bunları ses motorumuza bağlayalım.

Arayüz için Düzen Oluşturma

Synth'imizin sesini kontrol etmek için çeşitli form öğeleri oluşturacağız:

  • Osilatör tipini seçmek için bir radyo grubu
  • Filtreyi etkinleştirmek / devre dışı bırakmak için bir onay kutusu
  • Filtre türünü seçmek için bir radyo grubu
  • Filtrenin frekansını ve rezonansını kontrol etmek için iki aralık
  • Kazanç düğümünün saldırısını ve serbest bırakılmasını kontrol etmek için iki aralık

Arayüzümüz için bir HTML belgesi oluştururken, şunun gibi bir şeyle sonuçlanmalıyız:

 <div class="synth container" ng-controller="WebSynthCtrl"> <h1>webaudio synth</h1> <div class="form-group"> <select ng-model="activeDevice" class="form-control" ng-options="device.manufacturer + ' ' + device.name for device in devices"> <option value="" disabled>Choose a MIDI device...</option> </select> </div> <div class="col-lg-6 col-md-6 col-sm-6"> <h2>Oscillator</h2> <div class="form-group"> <h3>Oscillator Type</h3> <label ng-repeat="t in oscTypes"> <input type="radio" name="oscType" ng-model="synth.oscType" value="{{t}}" ng-checked="'{{t}}' === synth.oscType" /> {{t}} </label> </div> <h2>Filter</h2> <div class="form-group"> <label> <input type="checkbox" ng-model="synth.filterOn" /> enable filter </label> </div> <div class="form-group"> <h3>Filter Type</h3> <label ng-repeat="t in filterTypes"> <input type="radio" name="filterType" ng-model="synth.filterType" value="{{t}}" ng-disabled="!synth.filterOn" ng-checked="synth.filterOn && '{{t}}' === synth.filterType" /> {{t}} </label> </div> <div class="form-group"> <!-- frequency --> <label>filter frequency:</label> <input type="range" class="form-control" min="50" max="10000" ng-model="synth.filterFreq" ng-disabled="!synth.filterOn" /> </div> <div class="form-group"> <!-- resonance --> <label>filter resonance:</label> <input type="range" class="form-control" min="0" max="150" ng-model="synth.filterRes" ng-disabled="!synth.filterOn" /> </div> </div> <div class="col-lg-6 col-md-6 col-sm-6"> <div class="panel panel-default"> <div class="panel-heading">Analyser</div> <div class="panel-body"> <!-- frequency analyser --> <canvas></canvas> </div> </div> <div class="form-group"> <!-- attack --> <label>attack:</label> <input type="range" class="form-control" min="50" max="2500" ng-model="synth.attack" /> </div> <div class="form-group"> <!-- release --> <label>release:</label> <input type="range" class="form-control" min="50" max="1000" ng-model="synth.release" /> </div> </div> </div>

Kullanıcı arabirimini süslü görünecek şekilde dekore etmek, bu temel MIDI eğitiminde ele alacağım bir şey değil; bunun yerine, daha sonra kullanıcı arayüzünü cilalamak için bir alıştırma olarak kaydedebiliriz, belki de şöyle bir şeye benzemek için:

cilalı midi kullanıcı arayüzü

Arabirimi Ses Motoruna Bağlama

Bu kontrolleri ses motorumuza bağlamak için birkaç yöntem tanımlamalıyız.

Osilatörü Kontrol Etme

Osilatör için sadece osilatör tipini ayarlamamıza izin veren bir metoda ihtiyacımız var:

 Oscillator.prototype.setOscType = function(type) { if(type) { self.osc.type = type; } }

Filtreyi Kontrol Etme

Filtre için üç kontrole ihtiyacımız var: biri filtre tipi, biri frekans ve biri rezonans için. Ayrıca _connectFilter ve _disconnectFilter yöntemlerini onay kutusunun değerine bağlayabiliriz.

 Filter.prototype.setFilterType = function(type) { if(type) { self.filter.type = type; } } Filter.prototype.setFilterFrequency = function(freq) { if(freq) { self.filter.frequency.value = freq; } } Filter.prototype.setFilterResonance = function(res) { if(res) { self.filter.Q.value = res; } }

Saldırı ve Rezonansı Kontrol Etme

Sesimizi biraz şekillendirmek için kazanç düğümünün saldırı ve serbest bırakma parametrelerini değiştirebiliriz. Bunun için iki metoda ihtiyacımız var:

 function _setAttack(a) { if(a) { self.settings.attack = a / 1000; } } function _setRelease(r) { if(r) { self.settings.release = r / 1000; } }

İzleyicileri Ayarlama

Son olarak, uygulamamızın denetleyicisinde yalnızca birkaç izleyici kurmamız ve onları az önce oluşturduğumuz çeşitli yöntemlere bağlamamız gerekiyor:

 $scope.$watch('synth.oscType', DSP.setOscType); $scope.$watch('synth.filterOn', DSP.enableFilter); $scope.$watch('synth.filterType', DSP.setFilterType); $scope.$watch('synth.filterFreq', DSP.setFilterFrequency); $scope.$watch('synth.filterRes', DSP.setFilterResonance); $scope.$watch('synth.attack', DSP.setAttack); $scope.$watch('synth.release', DSP.setRelease);

Çözüm

Bu MIDI eğitiminde pek çok kavram ele alındı; Çoğunlukla, W3C'nin resmi spesifikasyonu dışında oldukça belgelenmemiş olan WebMIDI API'sinin nasıl kullanılacağını keşfettik. Google Chrome uygulaması oldukça basittir, ancak giriş ve çıkış cihazları için bir yineleyici nesneye geçiş, eski uygulamayı kullanan eski kod için biraz yeniden düzenleme gerektirir.

WebAudio API'ye gelince, bu çok zengin bir API'dir ve bu eğitimde sadece birkaç özelliğini ele aldık. WebMIDI API'sinden farklı olarak WebAudio API, özellikle Mozilla Developer Network'te çok iyi belgelenmiştir. Mozilla Developer Network, her bileşen için kendi özel tarayıcı tabanlı ses uygulamalarınızı uygulamanıza yardımcı olacak çok sayıda kod örneği ve çeşitli argümanların ve olayların ayrıntılı listelerini içerir.

Her iki API de büyümeye devam ettikçe, JavaScript geliştiricileri için çok ilginç olasılıklar sunacak; Flash eşdeğerleriyle rekabet edebilecek tam özellikli, tarayıcı tabanlı DAW geliştirmemize izin veriyor. Masaüstü geliştiricileri için, düğüm-webkit gibi araçları kullanarak kendi platformlar arası uygulamalarınızı oluşturmaya da başlayabilirsiniz. Umarım bu, fiziksel dünya ile bulut arasındaki boşluğu doldurarak kullanıcıları güçlendirecek, müzik tutkunları için yeni nesil müzik araçları üretecektir.