Oscillator with With Web Audio API + Vue.js

by bc_rikko

HTML

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
  <form class="controller-form">
    <div class="wave">
      <h3>Wave form</h3>
      <template v-for="wave in waveForms">
        <label>
          <input type="radio" :value="wave" v-model="form.waveForm">
          {{ wave }}
        </label>
      </template>
      <div v-if="form.waveForm === 'custom'" class="custom-form">
        <table>
          <thead>
            <th>harmonic</th>
            <th>real</th>
            <th>imag</th>
          </thead>
          <tbody>
            <!-- index0は0固定 -->
            <tr v-for="i in 7">
              <td>{{ i }}</td>
              <td><input type="range" min="0" max="1" step="any" v-model="form.harmonics.real[i]"></td>
              <td><input type="range" min="0" max="1" step="any" v-model="form.harmonics.imag[i]"></td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <div class="frequency">
      <h3>Frequency</h3>
      <input type="number" min="0" max="20000" v-model="form.freq">
    </div>
    <div class="detune">
      <h3>Detune</h3>
      <input type="range" min="-100" max="100" step="any" v-model="form.detune">
    </div>
  </form>
  <div class="osc-controller">
    <button type="button" @click="start">Start</button>
    <button type="button" @click="stop">Stop</button>
  </div>
</div>

JavaScript

// import vue@2.5.3

new Vue({
  el: '#app',
  data() {
    return {
      ctx: new AudioContext(),
      osc: null,
      form: {
        waveForm: 'sine',
        detune: 0,
        freq: 440,
        harmonics: {
          real: [0, 1, 0, 0, 0, 0, 0, 0],
          imag: [0, 0, 0, 0, 0, 0, 0, 0]
        }
      },
      waveForms: ['sine', 'square', 'sawtooth', 'triangle', 'custom']
    }
  },
  methods: {
    start() {
      this.osc = this.ctx.createOscillator();
      this.osc.connect(this.ctx.destination);
      this.osc.frequency.value = this.form.freq;
      this.osc.detune.value = this.form.detune;
      
      if (this.form.waveForm === 'custom') {
        // カスタム波形
        const wave = this.ctx.createPeriodicWave([...this.form.harmonics.real], [...this.form.harmonics.imag]);
        this.osc.setPeriodicWave(wave);
      } else {
        this.osc.type = this.form.waveForm;
      }
       
      this.osc.start();
    },
    stop() {
      this.osc.stop();
    }
  }
});