{"id":306198,"date":"2020-06-30T15:01:01","date_gmt":"2020-06-30T15:01:01","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=306198"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=306198","title":{"rendered":"\u041b\u0430\u0431\u0430\u0435\u043c \u043d\u0430 MIDI-\u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0435 \u0432 Angular"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/508788\/\">\n<p>Web MIDI API \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0437\u0432\u0435\u0440\u044c. \u0425\u043e\u0442\u044c \u043e\u043d \u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0443\u0436\u0435 \u043f\u043e\u0447\u0442\u0438 \u043f\u044f\u0442\u044c \u043b\u0435\u0442, \u0435\u0433\u043e \u0432\u0441\u0435 \u0435\u0449\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e Chromium. \u041d\u043e \u044d\u0442\u043e \u043d\u0435 \u043f\u043e\u043c\u0435\u0448\u0430\u0435\u0442 \u043d\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0435\u0437\u0430\u0442\u043e\u0440 \u0432 Angular. \u041f\u043e\u0440\u0430 \u043f\u043e\u0434\u043d\u044f\u0442\u044c Web Audio API \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c!<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/is\/hp\/yp\/ishpypqlmqkn8pe7eik5qpeg-9u.png\"><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p>\u0420\u0430\u043d\u0435\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u043f\u0440\u043e <a href=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/493622\/\">\u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 Web Audio API \u0432 Angular<\/a>. <\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u0443\u0437\u044b\u043a\u0443, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0432\u0435\u0441\u0435\u043b\u043e, \u043d\u043e \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0435\u0435 \u0438\u0433\u0440\u0430\u0442\u044c? \u0412 80-\u0435 \u0433\u043e\u0434\u044b \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u043e\u0431\u043c\u0435\u043d\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u043c\u0435\u0436\u0434\u0443 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u2014 MIDI. \u041e\u043d \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0438 \u043f\u043e \u0441\u0435\u0439 \u0434\u0435\u043d\u044c, \u0438 Chrome \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u043d\u0430 \u043d\u0430\u0442\u0438\u0432\u043d\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0438\u043d\u0442\u0435\u0437\u0430\u0442\u043e\u0440 \u0438\u043b\u0438 MIDI-\u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u0445 \u043a \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0443 \u0438 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u0432\u044b \u0438\u0433\u0440\u0430\u0435\u0442\u0435. \u041c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0441 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430, \u043f\u043e\u0441\u044b\u043b\u0430\u044f \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f, \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443 \u0432 Angular.<\/p>\n<p>  <\/p>\n<h2 id=\"web-midi-api\">Web MIDI API<\/h2>\n<p>  <\/p>\n<p>\u0412 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043d\u0435 \u0442\u0430\u043a \u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043d\u0430 \u0442\u0435\u043c\u0443 \u044d\u0442\u043e\u0433\u043e API, \u043d\u0435 \u0441\u0447\u0438\u0442\u0430\u044f <a href=\"https:\/\/www.w3.org\/TR\/webmidi\/\">\u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/a>. \u0412\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442\u0435 \u0434\u043e\u0441\u0442\u0443\u043f \u043a MIDI-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c \u0447\u0435\u0440\u0435\u0437 <code>navigator<\/code> \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0435 <code>Promise<\/code> \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0432\u0445\u043e\u0434\u0430\u043c\u0438 \u0438 \u0432\u044b\u0445\u043e\u0434\u0430\u043c\u0438. \u042d\u0442\u0438 \u0432\u0445\u043e\u0434\u044b \u0438 \u0432\u044b\u0445\u043e\u0434\u044b \u2014 \u0435\u0449\u0435 \u0438\u0445 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u043f\u043e\u0440\u0442\u0430\u043c\u0438 \u2014 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u043c\u0438 <code>EventTarget<\/code>\u0430\u043c\u0438. \u041e\u0431\u043c\u0435\u043d \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>MIDIMessageEvent<\/code>\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 <code>Uint8Array<\/code> \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f. \u0412 \u043a\u0430\u0436\u0434\u043e\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0438 \u043d\u0435 \u0431\u043e\u043b\u0435\u0435 3 \u0431\u0430\u0439\u0442. \u041f\u0435\u0440\u0432\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f status byte. \u041a\u0430\u0436\u0434\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0440\u043e\u043b\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 \u0438\u043b\u0438 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u0437\u0443\u043d\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u0436\u0430\u0442\u043e\u0439 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u0431\u0430\u0439\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0442\u043e, \u043a\u0430\u043a\u0430\u044f \u043a\u043b\u0430\u0432\u0438\u0448\u0430 \u043d\u0430\u0436\u0430\u0442\u0430, \u0430 \u0442\u0440\u0435\u0442\u0438\u0439 \u2014 \u043a\u0430\u043a \u0433\u0440\u043e\u043c\u043a\u043e \u043d\u043e\u0442\u0430 \u0431\u044b\u043b\u0430 \u0441\u044b\u0433\u0440\u0430\u043d\u0430. \u041f\u043e\u043b\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/www.midi.org\/specifications-old\/item\/table-2-expanded-messages-list-status-bytes\">\u043d\u0430 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0430\u0439\u0442\u0435 MIDI<\/a>. \u0412 Angular \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 <code>Observable<\/code>, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u0435\u0440\u0432\u044b\u043c \u0448\u0430\u0433\u043e\u043c \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 Web MIDI API \u043a RxJs.<\/p>\n<p>  <\/p>\n<h2 id=\"dependency-injection\">Dependency Injection<\/h2>\n<p>  <\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043c\u044b \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c <code>MIDIAccess<\/code>-\u043e\u0431\u044a\u0435\u043a\u0442, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0434\u043e \u043f\u043e\u0440\u0442\u043e\u0432. <code>navigator<\/code> \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0430\u043c <code>Promise<\/code>, \u0430 RxJs \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442 \u0435\u0433\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432 <code>Observable<\/code>. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e <code>InjectionToken<\/code>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <code>NAVIGATOR<\/code> \u0438\u0437 <a href=\"https:\/\/github.com\/ng-web-apis\/common\">@ng-web-apis\/common<\/a>. \u0422\u0430\u043a \u043c\u044b \u043d\u0435 \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0443 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">export const MIDI_ACCESS = new InjectionToken&lt;Promise&lt;MIDIAccess&gt;&gt;(    'Promise for MIDIAccess object',    {        factory: () =&gt; {            const navigatorRef = inject(NAVIGATOR);             return navigatorRef.requestMIDIAccess                ? navigatorRef.requestMIDIAccess()                : Promise.reject(new Error('Web MIDI API is not supported'));        },    }, );<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0432\u0441\u0435 MIDI-\u0441\u043e\u0431\u044b\u0442\u0438\u044f. \u041c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c <code>Observable<\/code> \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0434\u0432\u0443\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <code>Observable<\/code>, \u043a\u0430\u043a \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/501084\/\">Geolocation API<\/a><\/li>\n<li>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0441 \u0444\u0430\u0431\u0440\u0438\u043a\u043e\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 <code>Promise<\/code> \u0432 <code>Observable<\/code> \u0441\u043e\u0431\u044b\u0442\u0438\u0439<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439, \u0442\u043e\u043a\u0435\u043d \u0432\u043f\u043e\u043b\u043d\u0435 \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442. \u0421 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u043e\u0442\u043a\u0430\u0437\u0430 \u043a\u043e\u0434 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u043d\u0430 \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">export const MIDI_MESSAGES = new InjectionToken&lt;Observable&lt;MIDIMessageEvent&gt;&gt;(    'All incoming MIDI messages stream',    {        factory: () =&gt;            from(inject(MIDI_ACCESS).catch((e: Error) =&gt; e)).pipe(                switchMap(access =&gt;                    access instanceof Error                        ? throwError(access)                        : merge(                              ...Array.from(access.inputs).map(([_, input]) =&gt;                                  fromEvent(                                      input as FromEventTarget&lt;MIDIMessageEvent&gt;,                                      'midimessage',                                  ),                              ),                          ),                ),                share(),            ),    }, );<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043f\u043e\u0440\u0442, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0435\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0434\u043e\u0441\u0442\u0430\u043d\u0435\u043c \u0435\u0433\u043e \u0438\u0437 <code>MIDIAccess<\/code>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0442\u043e\u043a\u0435\u043d \u0438 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u0444\u0430\u0431\u0440\u0438\u043a\u0443 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">export function outputById(id: string): Provider[] {    return [        {            provide: MIDI_OUTPUT_QUERY,            useValue: id,        },        {            provide: MIDI_OUTPUT,            deps: [MIDI_ACCESS, MIDI_OUTPUT_QUERY],            useFactory: outputByIdFactory,        },    ]; }  export function outputByIdFactory(    midiAccess: Promise&lt;MIDIAccess&gt;,    id: string, ): Promise&lt;MIDIOutput | undefined&gt; {    return midiAccess.then(access =&gt; access.outputs.get(id)); }<\/code><\/pre>\n<p>  <\/p>\n<blockquote><p>\u041a\u0441\u0442\u0430\u0442\u0438, \u0432\u044b \u0437\u043d\u0430\u043b\u0438, \u0447\u0442\u043e \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0441\u043f\u0440\u044d\u0434\u0438\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 <code>Provider[]<\/code>, \u043a\u043e\u0433\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0435 \u0435\u0433\u043e \u0432 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435? \u041f\u043e\u043b\u0435 providers \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 <code>@Directive<\/code> \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043c\u043d\u043e\u0433\u043e\u043c\u0435\u0440\u043d\u044b\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u044b, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e:<\/p><\/blockquote>\n<p>  <\/p>\n<pre><code class=\"cs\">providers: [   outputById(\u2018someId\u2019),   ANOTHER_TOKEN,   SomeService, ]<\/code><\/pre>\n<p>  <\/p>\n<blockquote><p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u043d\u044b\u0435 \u043c\u0435\u043b\u043e\u0447\u0438 \u043f\u0440\u043e Angular \u2014 \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u044e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0430\u0448\u0443 <a href=\"https:\/\/twitter.com\/search?q=(from%3Awaterplea%2C%20OR%20from%3Amarsibarsi)%20%23AngularTip&amp;src=typed_query\">\u0441\u0435\u0440\u0438\u044e \u0442\u0432\u0438\u0442\u043e\u0432 \u0441 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c\u0438 \u0441\u043e\u0432\u0435\u0442\u0430\u043c\u0438<\/a>.<\/p><\/blockquote>\n<p>\u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u044b\u0432\u0430\u0442\u044c \u0438 \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u043f\u043e\u0440\u0442\u044b, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0438\u0445 \u043f\u043e \u0438\u043c\u0435\u043d\u0438.<\/p>\n<p>  <\/p>\n<h2 id=\"operatory\">\u041e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b<\/h2>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b. \u0412 \u043a\u043e\u043d\u0446\u0435 \u043a\u043e\u043d\u0446\u043e\u0432, \u043c\u044b \u0436\u0435 \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043a\u043e\u0432\u044b\u0440\u044f\u0442\u044c\u0441\u044f \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c \u043c\u0430\u0441\u0441\u0438\u0432\u0435 \u0434\u0430\u043d\u043d\u044b\u0445.<br \/>  \u041e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u043c\u043e\u0436\u043d\u043e \u0443\u0441\u043b\u043e\u0432\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043d\u0430 \u0434\u0432\u0435 \u0433\u0440\u0443\u043f\u043f\u044b:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0424\u0438\u043b\u044c\u0442\u0440\u0443\u044e\u0449\u0438\u0435. \u041e\u043d\u0438 \u043e\u0442\u0441\u0435\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0441 \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u043b\u0443\u0448\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u044b\u0433\u0440\u0430\u043d\u043d\u044b\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 \u0438\u043b\u0438 \u043f\u043e\u043b\u0437\u0443\u043d\u043e\u043a \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u0438. <\/li>\n<li>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0449\u0438\u0435. \u041e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u0430\u0441. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043e\u0442\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u044f \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0412\u043e\u0442 \u0442\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u043b\u0443\u0448\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u043a\u0430\u043d\u0430\u043b\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">export function filterByChannel(    channel: MidiChannel, ): MonoTypeOperatorFunction&lt;MIDIMessageEvent&gt; {    return source =&gt; source.pipe(filter(({data}) =&gt; data[0] % 16 === channel)); }<\/code><\/pre>\n<p>  <\/p>\n<p>Status byte \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d \u0433\u0440\u0443\u043f\u043f\u0430\u043c\u0438 \u043f\u043e 16: 128\u2014143 \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u0437\u0430 \u043d\u0430\u0436\u0430\u0442\u044b\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 (<code>noteOn<\/code>) \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0437 16 \u043a\u0430\u043d\u0430\u043b\u043e\u0432. 144\u2014159 \u2014 \u0437\u0430 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u0435 \u0437\u0430\u0436\u0430\u0442\u044b\u0445 \u043a\u043b\u0430\u0432\u0438\u0448 (<code>noteOff<\/code>). \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0435\u0441\u043b\u0438 \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043e\u0441\u0442\u0430\u0442\u043e\u043a \u043e\u0442 \u0434\u0435\u043b\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u0430\u0439\u0442\u0430 \u043d\u0430 16 \u2014 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043d\u043e\u043c\u0435\u0440 \u043a\u0430\u043d\u0430\u043b\u0430.<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u044b\u0433\u0440\u0430\u043d\u043d\u044b\u0435 \u043d\u043e\u0442\u044b, \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">export function notes(): MonoTypeOperatorFunction&lt;MIDIMessageEvent&gt; {    return source =&gt;        source.pipe(            filter(({data}) =&gt; between(data[0], 128, 159)),            map(event =&gt; {                if (between(event.data[0], 128, 143)) {                    event.data[0] += 16;                    event.data[2] = 0;                }                 return event;            }),        ); }<\/code><\/pre>\n<p>  <\/p>\n<blockquote><p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 MIDI-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u044f\u0432\u043d\u044b\u0435 <code>noteOff<\/code>-\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0443. \u041d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 <code>noteOn<\/code> \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u043d\u0443\u043b\u0435\u0432\u043e\u0439 \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c\u044e. \u042d\u0442\u043e\u0442 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0442\u0430\u043a\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u043f\u0440\u0438\u0432\u043e\u0434\u044f \u0432\u0441\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043a <code>noteOn<\/code>. \u041c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043c\u0435\u0449\u0430\u0435\u043c status byte \u043d\u0430 16, \u0447\u0442\u043e\u0431\u044b <code>noteOff<\/code>-\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0448\u043b\u0438 \u043d\u0430 \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u044e <code>noteOn<\/code>, \u0438 \u0437\u0430\u0434\u0430\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u0443\u044e \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c.<\/p><\/blockquote>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u0442\u0440\u0438\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">readonly notes$ = this.messages$.pipe(   catchError(() =&gt; EMPTY),   notes(),   toData(), );  constructor(   @Inject(MIDI_MESSAGES)   private readonly messages$: Observable&lt;MIDIMessageEvent&gt;, ) {}<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0440\u0430 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u044d\u0442\u043e \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435!<\/p>\n<p>  <\/p>\n<h2 id=\"sozdaem-sintezator\">\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0438\u043d\u0442\u0435\u0437\u0430\u0442\u043e\u0440<\/h2>\n<p>  <\/p>\n<p>\u0421 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/ng-web-apis\/audio\">\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f Web Audio API<\/a>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b <a href=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/493622\/\">\u043e\u0431\u0441\u0443\u0436\u0434\u0430\u043b\u0438 \u0440\u0430\u043d\u0435\u0435<\/a>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u0438\u044f\u0442\u043d\u043e \u0437\u0432\u0443\u0447\u0430\u0449\u0438\u0439 \u0441\u0438\u043d\u0442\u0435\u0437\u0430\u0442\u043e\u0440 \u0432\u0441\u0435\u0433\u043e \u0437\u0430 \u043f\u0430\u0440\u0443 \u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432. \u0417\u0430\u0442\u0435\u043c \u043c\u044b \u0441\u043a\u043e\u0440\u043c\u0438\u043c \u0435\u043c\u0443 \u043d\u043e\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0433\u0440\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435 \u0441\u0442\u0440\u0438\u043c.<\/p>\n<p>  <\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u0443\u0441\u043e\u043a \u043a\u043e\u0434\u0430. \u0427\u0442\u043e\u0431\u044b \u0441\u0438\u043d\u0442\u0435\u0437\u0430\u0442\u043e\u0440 \u0431\u044b\u043b \u043f\u043e\u043b\u0438\u0444\u043e\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u043c, \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0441\u044b\u0433\u0440\u0430\u043d\u043d\u044b\u0435 \u043d\u043e\u0442\u044b. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u043c scan:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">readonly notes$ = this.messages$.pipe(   catchError(() =&gt; EMPTY),   notes(),   toData(),   scan(     (map, [_, note, volume]) =&gt; map.set(note, volume), new Map()   ), );<\/code><\/pre>\n<p>  <\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0437\u0432\u0443\u043a \u043d\u0435 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u043b\u0441\u044f \u0440\u0435\u0437\u043a\u043e \u0438 \u043d\u0435 \u0437\u0432\u0443\u0447\u0430\u043b \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u0438, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 ADSR-\u043f\u0430\u0439\u043f. \u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u044b\u043b\u0430 \u0435\u0433\u043e \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f. \u041d\u0430\u043f\u043e\u043c\u043d\u044e, \u0438\u0434\u0435\u044f ADSR \u2014 \u043c\u0435\u043d\u044f\u0442\u044c \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c \u0437\u0432\u0443\u043a\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bk\/1i\/49\/bk1i49nn7x4rniv_xmwn8a8zhgg.png\"><\/p>\n<p>  <\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u043e\u0442\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u043b\u0430\u0441\u044c \u043d\u0435 \u0440\u0435\u0437\u043a\u043e, \u0443\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u043b\u0430\u0441\u044c \u043d\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u0438, \u043f\u043e\u043a\u0430 \u043a\u043b\u0430\u0432\u0438\u0448\u0430 \u043d\u0430\u0436\u0430\u0442\u0430, \u0430 \u043f\u043e\u0442\u043e\u043c \u043f\u043b\u0430\u0432\u043d\u043e \u0437\u0430\u0442\u0443\u0445\u0430\u043b\u0430.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">@Pipe({     name: 'adsr', }) export class AdsrPipe implements PipeTransform {     transform(         value: number,         attack: number,         decay: number,         sustain: number,         release: number,     ): AudioParamInput {         return value             ? [                   {                       value: 0,                       duration: 0,                       mode: 'instant',                   },                   {                       value,                       duration: attack,                       mode: 'linear',                   },                   {                       value: sustain,                       duration: decay,                       mode: 'linear',                   },               ]             : {                   value: 0,                   duration: release,                   mode: 'linear',               };     } }<\/code><\/pre>\n<p>  <\/p>\n<blockquote><p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043a\u043b\u0430\u0432\u0438\u0448\u0443, \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u043b\u0438\u043d\u0435\u0439\u043d\u043e \u043d\u0430\u0440\u0430\u0441\u0442\u0430\u0442\u044c \u0437\u0430 \u0432\u0440\u0435\u043c\u044f attack. \u0417\u0430\u0442\u0435\u043c \u043e\u043d\u0430 \u0443\u0431\u0430\u0432\u0438\u0442\u0441\u044f \u0434\u043e \u0443\u0440\u043e\u0432\u043d\u044f sustain \u0437\u0430 \u0432\u0440\u0435\u043c\u044f decay. \u0410 \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043e\u0442\u043f\u0443\u0441\u0442\u0438\u043c \u043a\u043b\u0430\u0432\u0438\u0448\u0443, \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c \u0443\u043f\u0430\u0434\u0435\u0442 \u0434\u043e \u043d\u0443\u043b\u044f \u0437\u0430 \u0432\u0440\u0435\u043c\u044f release.<\/p><\/blockquote>\n<p>\u0421 \u0442\u0430\u043a\u0438\u043c \u043f\u0430\u0439\u043f\u043e\u043c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0431\u0440\u043e\u0441\u0430\u0442\u044c \u0441\u0438\u043d\u0442\u0435\u0437\u0430\u0442\u043e\u0440 \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"html\">&lt;ng-container   *ngFor=&quot;let note of notes | keyvalue; trackBy: noteKey&quot; &gt;   &lt;ng-container     waOscillatorNode     detune=&quot;5&quot;     autoplay     [frequency]=&quot;toFrequency(note.key)&quot;    &gt;     &lt;ng-container        waGainNode        gain=&quot;0&quot;       [gain]=&quot;note.value | adsr: 0:0.1:0.02:1&quot;     &gt;       &lt;ng-container waAudioDestinationNode&gt;&lt;\/ng-container&gt;     &lt;\/ng-container&gt;   &lt;\/ng-container&gt;    &lt;ng-container     waOscillatorNode     type=&quot;sawtooth&quot;     autoplay      [frequency]=&quot;toFrequency(note.key)&quot;   &gt;     &lt;ng-container        waGainNode       gain=&quot;0&quot;       [gain]=&quot;note.value | adsr: 0:0.1:0.02:1&quot;     &gt;       &lt;ng-container waAudioDestinationNode&gt;&lt;\/ng-container&gt;       &lt;ng-container [waOutput]=&quot;convolver&quot;&gt;&lt;\/ng-container&gt;     &lt;\/ng-container&gt;   &lt;\/ng-container&gt; &lt;\/ng-container&gt; &lt;ng-container   #convolver=&quot;AudioNode&quot;   waConvolverNode   buffer=&quot;assets\/audio\/response.wav&quot; &gt;   &lt;ng-container waAudioDestinationNode&gt;&lt;\/ng-container&gt; &lt;\/ng-container&gt;<\/code><\/pre>\n<p>  <\/p>\n<p>\u041c\u044b \u043f\u0435\u0440\u0435\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043d\u043e\u0442\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0433\u043e <code>keyvalue<\/code> \u043f\u0430\u0439\u043f\u0430, \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u044f \u0438\u0445 \u043f\u043e \u043d\u043e\u043c\u0435\u0440\u0443 \u0441\u044b\u0433\u0440\u0430\u043d\u043d\u043e\u0439 \u043a\u043b\u0430\u0432\u0438\u0448\u0438. \u0417\u0430\u0442\u0435\u043c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u043e\u0441\u0446\u0438\u043b\u043b\u044f\u0442\u043e\u0440\u0430, \u0438\u0433\u0440\u0430\u044e\u0449\u0438\u0445 \u043d\u0443\u0436\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u043e\u0442\u044b. \u0410 \u0432 \u043a\u043e\u043d\u0446\u0435 \u2014 \u044d\u0444\u0444\u0435\u043a\u0442 \u0440\u0435\u0432\u0435\u0440\u0431\u0435\u0440\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>ConvolverNode<\/code>. \u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043d\u0435\u0445\u0438\u0442\u0440\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0438 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u043d\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0445\u043e\u0440\u043e\u0448\u043e \u0437\u0432\u0443\u0447\u0430\u0449\u0438\u0439, \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u0435\u043c\u043e \u0432 Chrome:<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/ng-web-apis.github.io\/midi\">https:\/\/ng-web-apis.github.io\/midi<\/a><\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 MIDI-\u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u044b \u2014 \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043d\u0430\u0436\u0438\u043c\u0430\u0442\u044c \u043d\u0430 \u043d\u043e\u0442\u044b \u043c\u044b\u0448\u043a\u043e\u0439.<\/p>\n<p>  <\/p>\n<blockquote><p>\u0416\u0438\u0432\u043e\u0435 \u0434\u0435\u043c\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0442\u0443\u0442, \u043e\u0434\u043d\u0430\u043a\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a MIDI \u0432 iframe: <a href=\"https:\/\/stackblitz.com\/edit\/angular-midi\">https:\/\/stackblitz.com\/edit\/angular-midi<\/a><\/p><\/blockquote>\n<p>  <\/p>\n<h2 id=\"zaklyuchenie\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>  <\/p>\n<p>\u0412 Angular \u043c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e RxJs. \u0418 Web MIDI API \u043d\u0435 \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 DOM-\u0441\u043e\u0431\u044b\u0442\u0438\u0439. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u0440\u044b \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u043c\u044b \u0441\u043c\u043e\u0433\u043b\u0438 \u0441 \u043b\u0435\u0433\u043a\u043e\u0441\u0442\u044c\u044e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 MIDI \u0432 \u043d\u0430\u0448\u0435 Angular \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0432 \u0432\u0438\u0434\u0435 open-source \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/ng-web-apis\/midi\">@ng-web-apis\/midi<\/a>. \u041e\u043d\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u044c\u044e \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043f\u043e\u0434 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c Web APIs for Angular. \u041d\u0430\u0448\u0430 \u0446\u0435\u043b\u044c \u2014 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u044b\u0445 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u043e\u0431\u0435\u0440\u0442\u043e\u043a \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e API \u0432 Angular \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445. \u0422\u0430\u043a \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u0435\u043d, \u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443, <a href=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/492700\/\">Payment Request API<\/a> \u0438\u043b\u0438 Intersection Observer \u2014 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 <a href=\"https:\/\/ng-web-apis.github.io\/\">\u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u0440\u0435\u043b\u0438\u0437\u044b<\/a>.<\/p>\n<p>  <\/p>\n<p><strong>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043b\u044e\u0431\u043e\u043f\u044b\u0442\u043d\u043e, \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430 Angular \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 Web MIDI API \u2014 \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u044e \u0432\u0430\u0441 \u043d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0438\u0433\u0440\u0430\u0442\u044c \u043d\u0430 \u043a\u043b\u0430\u0432\u0438\u0448\u0430\u0445 \u0432 \u043b\u0438\u0447\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 <a href=\"https:\/\/jamigo.app\/\">Jamigo.app<\/a><\/strong><\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/508788\/\"> https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/508788\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/508788\/\">\n<p>Web MIDI API \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0437\u0432\u0435\u0440\u044c. \u0425\u043e\u0442\u044c \u043e\u043d \u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0443\u0436\u0435 \u043f\u043e\u0447\u0442\u0438 \u043f\u044f\u0442\u044c \u043b\u0435\u0442, \u0435\u0433\u043e \u0432\u0441\u0435 \u0435\u0449\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e Chromium. \u041d\u043e \u044d\u0442\u043e \u043d\u0435 \u043f\u043e\u043c\u0435\u0448\u0430\u0435\u0442 \u043d\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0435\u0437\u0430\u0442\u043e\u0440 \u0432 Angular. \u041f\u043e\u0440\u0430 \u043f\u043e\u0434\u043d\u044f\u0442\u044c Web Audio API \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c!<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/is\/hp\/yp\/ishpypqlmqkn8pe7eik5qpeg-9u.png\"><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-306198","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/306198","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=306198"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/306198\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=306198"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=306198"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=306198"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}