WEB-MIDI-API BROWSER-API WEB-AUDIO WEB-DEVELOPMENT REAL-TIME INTERACTIVITY JAVASCRIPT MUSIC-TECH HARDWARE-INTERACTION

Web MIDI API: Menghubungkan Aplikasi Web Anda dengan Dunia Musik Digital

⏱️ 13 menit baca
👨‍💻

Web MIDI API: Menghubungkan Aplikasi Web Anda dengan Dunia Musik Digital

1. Pendahuluan

Pernah membayangkan bisa mengubah browser Anda menjadi studio musik mini, lengkap dengan keyboard MIDI, drum pad, atau bahkan sequencer canggih? Atau mungkin Anda ingin membuat aplikasi web yang merespons setiap sentuhan pada kontroler MIDI fisik Anda? Jika ya, maka artikel ini untuk Anda!

Dalam dunia web development, kita sering berinteraksi dengan API yang berhubungan dengan visual, teks, atau data. Namun, ada satu API yang membuka dimensi baru interaksi: Web MIDI API. API ini memungkinkan aplikasi web Anda untuk berkomunikasi langsung dengan perangkat Musical Instrument Digital Interface (MIDI) yang terhubung ke komputer pengguna.

MIDI adalah protokol standar industri yang digunakan untuk menghubungkan instrumen musik elektronik, komputer, dan perangkat audio lainnya. Dengan Web MIDI API, kita bisa membaca input dari keyboard MIDI fisik dan bahkan mengirimkan output ke perangkat MIDI lainnya, semuanya dari dalam browser! Ini membuka pintu ke berbagai aplikasi kreatif, mulai dari instrumen virtual, editor musik, hingga tools untuk live performance yang berjalan sepenuhnya di web.

Mengapa topik ini penting untuk developer Indonesia?

Mari kita selami lebih dalam bagaimana Web MIDI API bekerja dan bagaimana Anda bisa mulai menggunakannya dalam proyek web Anda!

2. Memulai dengan Web MIDI API: Meminta Izin Akses

Seperti banyak API browser yang berinteraksi dengan hardware, Web MIDI API memerlukan izin dari pengguna. Ini adalah langkah keamanan penting untuk memastikan privasi dan kontrol pengguna.

Untuk memulai, kita perlu memanggil navigator.requestMIDIAccess(). Fungsi ini akan mengembalikan sebuah Promise yang akan me-resolve dengan objek MIDIAccess jika izin diberikan, atau me-reject jika izin ditolak atau terjadi error.

async function requestMidiAccess() {
  try {
    const midiAccess = await navigator.requestMIDIAccess();
    console.log('Akses MIDI berhasil diberikan!', midiAccess);
    // Lanjutkan dengan logika aplikasi MIDI Anda di sini
    listMidiDevices(midiAccess);
  } catch (error) {
    console.error('Gagal mendapatkan akses MIDI:', error);
    alert('Aplikasi ini memerlukan akses ke perangkat MIDI Anda. Mohon izinkan.');
  }
}

// Panggil saat aplikasi dimulai, misalnya setelah user berinteraksi
// atau pada event DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
  const button = document.getElementById('requestMidiButton');
  if (button) {
    button.addEventListener('click', requestMidiAccess);
  } else {
    // Jika tidak ada tombol, coba minta akses langsung
    // (disarankan ada interaksi user dulu untuk UX yang baik)
    // requestMidiAccess();
    console.warn("Tombol 'requestMidiButton' tidak ditemukan. Pastikan ada interaksi pengguna sebelum meminta akses MIDI.");
  }
});

📌 Catatan Penting:

Setelah mendapatkan objek MIDIAccess, kita bisa mulai menjelajahi perangkat MIDI yang terhubung.

3. Mengakses Input dan Output MIDI: Menjelajahi Perangkat

Objek MIDIAccess memiliki dua properti penting: inputs dan outputs. Keduanya adalah MIDIInputMap dan MIDIOutputMap, yang pada dasarnya adalah Map dari ID perangkat ke objek MIDIInput atau MIDIOutput.

Kita bisa mengiterasi map ini untuk melihat perangkat yang tersedia.

function listMidiDevices(midiAccess) {
  console.log('--- Perangkat Input MIDI ---');
  if (midiAccess.inputs.size === 0) {
    console.log('Tidak ada perangkat input MIDI terdeteksi.');
  } else {
    midiAccess.inputs.forEach((input) => {
      console.log(`ID: ${input.id}, Nama: ${input.name}, Manufaktur: ${input.manufacturer}, Status: ${input.state}`);
    });
  }

  console.log('\n--- Perangkat Output MIDI ---');
  if (midiAccess.outputs.size === 0) {
    console.log('Tidak ada perangkat output MIDI terdeteksi.');
  } else {
    midiAccess.outputs.forEach((output) => {
      console.log(`ID: ${output.id}, Nama: ${output.name}, Manufaktur: ${output.manufacturer}, Status: ${output.state}`);
    });
  }

  // Kita juga bisa mendengarkan perubahan pada perangkat (misal: perangkat dicolok/dicabut)
  midiAccess.onstatechange = (event) => {
    console.log(`Perangkat MIDI berubah: ${event.port.name} (tipe: ${event.port.type}, status: ${event.port.state})`);
    listMidiDevices(midiAccess); // Refresh daftar perangkat
  };
}

💡 Tips: Properti name, manufacturer, dan state sangat berguna untuk menampilkan daftar perangkat yang ramah pengguna di UI Anda. Properti id adalah identifier unik yang bisa Anda gunakan untuk memilih perangkat tertentu.

4. Mendengarkan Event MIDI dari Perangkat Input

Setelah kita mengidentifikasi perangkat input MIDI, langkah selanjutnya adalah mendengarkan pesan MIDI yang datang dari perangkat tersebut. Setiap objek MIDIInput memiliki event handler onmidimessage. Event ini akan terpicu setiap kali perangkat MIDI mengirimkan pesan.

Data MIDI yang diterima adalah Uint8Array yang berisi satu atau lebih pesan MIDI. Pesan MIDI biasanya terdiri dari 3 byte:

  1. Status Byte: Menunjukkan jenis pesan (misal: Note On, Note Off, Control Change).
  2. Data Byte 1: Parameter pertama (misal: nomor nada untuk Note On/Off).
  3. Data Byte 2: Parameter kedua (misal: kecepatan atau “velocity” untuk Note On/Off).

Mari kita buat contoh sederhana untuk mendeteksi Note On dan Note Off dari keyboard MIDI:

<!-- index.html -->
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web MIDI Piano Sederhana</title>
    <style>
        body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; margin-top: 50px; }
        #status { margin-top: 20px; font-size: 1.2em; }
        .note-indicator {
            width: 50px; height: 50px; border-radius: 50%; background-color: lightgray;
            display: inline-flex; justify-content: center; align-items: center;
            margin: 5px; font-weight: bold; transition: background-color 0.1s;
        }
        .note-indicator.active { background-color: dodgerblue; color: white; }
    </style>
</head>
<body>
    <h1>Web MIDI Piano Sederhana</h1>
    <button id="requestMidiButton">Minta Akses MIDI</button>
    <div id="status">Status: Belum terhubung</div>
    <div id="noteIndicators"></div>

    <script src="app.js"></script>
</body>
</html>
// app.js
let midiAccess;
const statusDiv = document.getElementById('status');
const noteIndicatorsDiv = document.getElementById('noteIndicators');

// Buat indikator nada untuk beberapa oktaf
const noteNames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
for (let i = 0; i < 88; i++) { // Asumsi 88 tuts piano (MIDI notes 21-108)
    const noteNumber = 21 + i;
    const noteName = noteNames[noteNumber % 12];
    const octave = Math.floor(noteNumber / 12) - 1; // MIDI note 0 = C-1
    const indicator = document.createElement('div');
    indicator.classList.add('note-indicator');
    indicator.id = `note-${noteNumber}`;
    indicator.textContent = `${noteName}${octave}`;
    noteIndicatorsDiv.appendChild(indicator);
}


async function requestMidiAccess() {
  if (midiAccess) {
    console.log("Akses MIDI sudah diberikan.");
    return;
  }
  try {
    midiAccess = await navigator.requestMIDIAccess();
    statusDiv.textContent = 'Status: Terhubung! Menunggu input MIDI...';
    console.log('Akses MIDI berhasil diberikan!', midiAccess);
    setupMidiInput(midiAccess);
  } catch (error) {
    console.error('Gagal mendapatkan akses MIDI:', error);
    statusDiv.textContent = 'Status: Gagal terhubung. Pastikan HTTPS dan izinkan akses MIDI.';
    alert('Aplikasi ini memerlukan akses ke perangkat MIDI Anda. Mohon izinkan.');
  }
}

function setupMidiInput(midiAccess) {
  if (midiAccess.inputs.size === 0) {
    statusDiv.textContent = 'Status: Terhubung, tapi tidak ada perangkat input MIDI terdeteksi.';
    return;
  }

  midiAccess.inputs.forEach((input) => {
    console.log(`Mendengarkan input dari: ${input.name}`);
    input.onmidimessage = handleMidiMessage;
  });

  midiAccess.onstatechange = (event) => {
    console.log(`Perangkat MIDI berubah: ${event.port.name} (tipe: ${event.port.type}, status: ${event.port.state})`);
    if (event.port.type === 'input') {
      // Perangkat input berubah, mungkin perlu setup ulang
      midiAccess.inputs.forEach((input) => {
        // Pastikan hanya satu listener per input
        input.onmidimessage = null;
        input.onmidimessage = handleMidiMessage;
      });
      statusDiv.textContent = 'Status: Perangkat MIDI berubah. Menunggu input...';
    }
  };
}

function handleMidiMessage(event) {
  const [command, noteNumber, velocity] = event.data;
  const noteIndicator = document.getElementById(`note-${noteNumber}`);

  switch (command & 0xF0) { // Masking untuk mendapatkan tipe command tanpa channel
    case 0x90: // Note On (0x90-0x9F adalah Note On untuk channel 0-15)
      if (velocity > 0) { // Note On dengan velocity > 0
        console.log(`Note ON: ${noteNumber}, Velocity: ${velocity}`);
        if (noteIndicator) {
            noteIndicator.classList.add('active');
        }
      } else { // Note On dengan velocity 0 sama dengan Note Off
        console.log(`Note OFF (velocity 0): ${noteNumber}`);
        if (noteIndicator) {
            noteIndicator.classList.remove('active');
        }
      }
      break;
    case 0x80: // Note Off (0x80-0x8F adalah Note Off untuk channel 0-15)
      console.log(`Note OFF: ${noteNumber}, Velocity: ${velocity}`);
      if (noteIndicator) {
          noteIndicator.classList.remove('active');
      }
      break;
    case 0xB0: // Control Change
      console.log(`Control Change: Controller ${noteNumber}, Value: ${velocity}`);
      break;
    // Tambahkan case lain untuk pesan MIDI lainnya jika diperlukan
    default:
      console.log(`Pesan MIDI lain: ${command}, ${noteNumber}, ${velocity}`);
      break;
  }
}

document.addEventListener('DOMContentLoaded', () => {
  document.getElementById('requestMidiButton').addEventListener('click', requestMidiAccess);
});

Pesan MIDI Penting:

Dengan kode di atas, Anda bisa menghubungkan keyboard MIDI Anda, menekan tuts, dan melihat indikator nada di browser menyala dan mati sesuai dengan input Anda!

5. Mengirim Pesan MIDI ke Perangkat Output

Selain menerima input, Web MIDI API juga memungkinkan kita untuk mengirim pesan MIDI ke perangkat output. Ini sangat berguna untuk mengontrol instrumen virtual, synthesizer hardware, atau bahkan lampu DMX jika terhubung melalui MIDI.

Setiap objek MIDIOutput memiliki method send(). Method ini menerima Uint8Array yang sama dengan format pesan MIDI yang kita terima dari input.

// Lanjutan dari app.js, tambahkan fungsi ini
function playNote(output, noteNumber, velocity = 100, duration = 500) {
  // Note On
  output.send([0x90, noteNumber, velocity]);
  console.log(`Mengirim Note ON: ${noteNumber} ke ${output.name}`);

  // Note Off setelah durasi tertentu
  setTimeout(() => {
    output.send([0x80, noteNumber, 0]); // Velocity 0 untuk Note Off
    console.log(`Mengirim Note OFF: ${noteNumber} ke ${output.name}`);
  }, duration);
}

// Contoh penggunaan:
// Asumsi Anda sudah mendapatkan midiAccess dan memilih output
// const selectedOutput = Array.from(midiAccess.outputs.values())[0]; // Pilih output pertama
// if (selectedOutput) {
//   playNote(selectedOutput, 60, 127, 1000); // Mainkan C4 selama 1 detik
// }

🎯 Studi Kasus: Bayangkan Anda membuat sequencer web. Anda bisa menggunakan setInterval atau requestAnimationFrame untuk memicu playNote pada interval waktu tertentu, menciptakan pola melodi atau ritme yang kompleks. Anda juga bisa mengirim pesan Control Change untuk mengatur parameter suara pada synthesizer eksternal, membuka potensi otomatisasi yang luar biasa.

⚠️ Peringatan: Pastikan Anda selalu mengirim pesan Note Off setelah Note On. Jika tidak, nada akan terus berbunyi tanpa henti (hang note) di perangkat MIDI Anda, yang bisa sangat mengganggu!

6. Kasus Penggunaan Nyata dan Pertimbangan Penting

Web MIDI API membuka banyak kemungkinan menarik:

Pertimbangan Penting:

Web MIDI API adalah jembatan yang kuat antara dunia web dan dunia musik digital. Dengan sedikit JavaScript, Anda bisa membuka pintu ke kreasi yang sebelumnya hanya mungkin dengan aplikasi desktop.

Kesimpulan

Web MIDI API adalah salah satu permata tersembunyi di ekosistem web yang memungkinkan interaksi mendalam antara aplikasi web dan perangkat musik digital. Dari mengaktifkan instrumen virtual hingga mengendalikan synthesizer hardware, potensi kreativitas yang ditawarkannya sangat luas.

Meskipun masih memerlukan dukungan penuh di semua browser, API ini sudah sangat praktis untuk proyek-proyek yang menargetkan pengguna Chrome/Edge atau dalam konteks PWA. Dengan memahami cara meminta izin, membaca input, dan mengirim output MIDI, Anda kini memiliki fondasi untuk membangun pengalaman musik interaktif yang unik langsung di browser.

Jadi, tunggu apa lagi? Ambil keyboard MIDI Anda, buka editor kode, dan mulailah berkreasi! Dunia musik digital di web menanti sentuhan inovatif Anda.

🔗 Baca Juga