Spaces:
Running
Running
| import { Measure } from "../measure/Measure" | |
| export interface Beat { | |
| measure: number | |
| beat: number | |
| tick: number | |
| } | |
| export type BeatWithX = Beat & { | |
| x: number | |
| } | |
| // 範囲内の measure を探す。最初の要素は startTick 以前のものも含む | |
| // Find Measure within the range.The first element also includes something before StartTick | |
| const getMeasuresInRange = ( | |
| measures: Measure[], | |
| startTick: number, | |
| endTick: number, | |
| ) => { | |
| let i = 0 | |
| const result: Measure[] = [] | |
| for (const measure of measures) { | |
| const nextMeasure = measures[i + 1] | |
| i++ | |
| // 最初の measure を探す | |
| // Find the first MEASURE | |
| if (result.length === 0) { | |
| if (nextMeasure !== undefined && nextMeasure.startTick <= startTick) { | |
| continue // 次の measure が最初になりうる場合はスキップ | |
| // Skip if the next Measure can be the first | |
| } | |
| if (measure.startTick > startTick) { | |
| console.warn("There is no initial time signature. Use 4/4 by default") | |
| result.push({ startTick: 0, measure: 0, numerator: 4, denominator: 4 }) | |
| } else { | |
| result.push(measure) | |
| } | |
| } | |
| // 残りの measure を探す. 最初の measure がない場合に正しく処理できるように else ではなくもう一度最初の measure があるか調べる | |
| // Find the remaining MEASURE. If you can handle correctly if you do not have the first MEASURE, check if you do the first MEASURE, rather than ELSE | |
| if (result.length !== 0) { | |
| if (measure.startTick <= endTick) { | |
| result.push(measure) | |
| } else { | |
| break | |
| } | |
| } | |
| } | |
| return result | |
| } | |
| export const createBeatsInRange = ( | |
| allMeasures: Measure[], | |
| timebase: number, | |
| startTick: number, | |
| endTick: number, | |
| ): Beat[] => { | |
| const beats: Beat[] = [] | |
| const measures = getMeasuresInRange(allMeasures, startTick, endTick) | |
| measures.forEach((measure, i) => { | |
| const nextMeasure = measures[i + 1] | |
| const ticksPerBeat = (timebase * 4) / measure.denominator | |
| // 次の小節か曲の endTick まで拍を作る | |
| // Make a beat up to the next bar or song EndTick | |
| const lastTick = nextMeasure ? nextMeasure.startTick : endTick | |
| const startBeat = Math.max( | |
| 0, | |
| Math.floor((startTick - measure.startTick) / ticksPerBeat), | |
| ) | |
| const endBeat = (lastTick - measure.startTick) / ticksPerBeat | |
| for (let beat = startBeat; beat < endBeat; beat++) { | |
| const tick = measure.startTick + ticksPerBeat * beat | |
| beats.push({ | |
| measure: measure.measure + Math.floor(beat / measure.numerator), | |
| beat: beat % measure.numerator, | |
| tick, | |
| }) | |
| } | |
| }) | |
| return beats | |
| } | |
| export const createBeatsWithXInRange = ( | |
| allMeasures: Measure[], | |
| pixelsPerTick: number, | |
| timebase: number, | |
| startTick: number, | |
| width: number, | |
| ): BeatWithX[] => { | |
| const endTick = startTick + width / pixelsPerTick | |
| return createBeatsInRange(allMeasures, timebase, startTick, endTick).map( | |
| (b) => ({ | |
| ...b, | |
| x: Math.round(b.tick * pixelsPerTick), | |
| }), | |
| ) | |
| } | |