| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- /**
- *
- * 修改自:
- * https://github.com/zya/wa-metro/blob/master/lib/wa-metro.js
- *
- */
- import runtime, * as RunTimeUtils from '/src/pages/detail/runtime'
- import state from '/src/pages/detail/state'
- type ICallback = (time?: number, step?: number, scheduler_interval?: number, context?: AudioContext) => void
- type IOptions = {
- steps: number
- tempo: number
- denominator: number
- numerator: number
- limit: number
- onStop?: () => void
- onTick?: (tick?: number) => void
- }
- export const ac = (window.AudioContext || (window as any).webkitAudioContext || (window as any).mozAudioContext || (window as any).msAudioContext)
- class Metro {
- audio: HTMLAudioElement = new Audio()//runtime.audiosInstance.audio.cloneNode(true)
- context = new ac()
- callback: ICallback = () => {}
- look_ahead = 1.0
- _step = 1
- _scheduler_interval = 20
- _next_event_time = 0.0
- _first = true
- _is_running = false
- timer: number = -1
- tiems: number = 0
- tick: number = 0
- options: IOptions = {
- steps: 4,
- tempo: 90,
- denominator: 4,
- numerator: 4,
- limit: 0,
- onStop: () => {},
- onTick: () => {}
- }
- constructor(options?: IOptions) {
- this.options = {
- ...this.options,
- ...options
- }
- }
- refreshTick = () => {
- RunTimeUtils.refreshTick(this.audio.currentTime)
- if (this.audio.currentTime >= 4.8) {
- this.stop()
- }
- }
- start = (options?: IOptions) => {
- // state.showTick = true
- this.audio.play()
- this.audio.addEventListener('timeupdate', this.refreshTick)
- // if (this._is_running) {
- // console.log('already started');
- // return;
- // }
- // this._step = 1
- this.options = {
- ...this.options,
- ...options
- }
- // this._is_running = true;
- // this.timer = setInterval(() => {
- // this._scheduler()
- // }, 25)
- // this._worker.postMessage('start');
- };
- pause = () => {
- this._is_running = false;
- // this._worker.postMessage('stop');
- this.stop()
- };
- stop = (dontCall?: boolean) => {
- // state.showTick = false
- this.audio.pause()
- this.audio.removeEventListener('timeupdate', this.refreshTick)
- if (this.audio.currentTime >= 4.8) {
- this.options.onStop && this.options.onStop()
- }
- runtime.osmd.cursor.hide()
- this.audio.currentTime = 0
- this.refreshTick()
- // this._first = true;
- // this._step = 1;
- // this._is_running = false;
- // clearInterval(this.timer)
- // this.timer = -1
- // this.tiems = 0
- // this.tick = 0
- // this.context = new ac()
- // if (dontCall !== true) {
- // setTimeout(() => {
- // this._is_running = false;
- // this.options.onStop && this.options.onStop()
- // this.context.close()
- // }, 500)
- // } else {
- // // this.context.close()
- // }
- // this._worker.postMessage('stop');
- };
- scheduleNote = (beatNumber: number, time: number) => {
- // create an oscillator
- var osc = this.context.createOscillator();
- osc.connect( this.context.destination );
- // if (beatNumber % 16 === 0) // beat 0 == low pitch
- // osc.frequency.value = 880.0;
- // osc.type = 'sine';
- // osc.detune.value = -300;
- if (beatNumber === 1) {
- osc.frequency.value = 880.0;
- } else {
- osc.frequency.value = 440.0;
- }
- osc.start( time );
- osc.stop( time + 0.05 );
- // osc.onended = () => {
- // console.log(time)
- // this.tick ++
- // this.options.onTick && this.options.onTick(this.tick);
- // // if (this.options.limit > 0 && this.tick === this.options.limit * this.options.numerator) {
- // // this.options.onStop && this.options.onStop()
- // // }
- // }
- }
- _scheduler = () => {
- var self = this;
- if (this._step === 1 && this._first) {
- this._next_event_time = this.context.currentTime;
- }
- while (this._next_event_time < this.context.currentTime + this.look_ahead && this._is_running) {
- if (this.options.limit > 0 && this.tiems >= this.options.limit) {
- this.stop()
- break
- }
- if (this._step === this.options.numerator) {
- this.tiems ++
- }
- this.options.onTick && this.options.onTick(this._step);
- // var event_time_from_scheduled = self._next_event_time - self.context.currentTime;
- this.scheduleNote(this._step, self._next_event_time)
- this._next();
- }
- };
- _next = () => {
- this._step++;
- if (this._first) {
- this._next_event_time = this.context.currentTime;
- this._first = false;
- }
- // if (this._step > this.options.numerator) {
- // this._step = 1;
- // }
- this._next_event_time += ((60.0 / this.options.tempo) * 4) / this.options.denominator;
- };
- }
- export default Metro
|