| 116 | } |
| 117 | |
| 118 | private async draw(backingTrack: alphaTab.model.BackingTrack): Promise<void> { |
| 119 | const buffer = backingTrack.rawAudioFile; |
| 120 | if (!buffer) { |
| 121 | return; |
| 122 | } |
| 123 | |
| 124 | const audioContext = new AudioContext(); |
| 125 | const data = await audioContext.decodeAudioData(structuredClone(buffer.buffer) as ArrayBuffer); |
| 126 | |
| 127 | const top = data.getChannelData(0); |
| 128 | const bottom = data.numberOfChannels > 1 ? data.getChannelData(1) : top; |
| 129 | const length = top.length; |
| 130 | |
| 131 | if (!this.canvas) { |
| 132 | this.canvas = document.createElement('canvas'); |
| 133 | this.root.insertBefore(this.canvas, this.cursor); |
| 134 | } |
| 135 | const width = this.root.offsetWidth || 600; |
| 136 | const height = 80; |
| 137 | this.canvas.width = width; |
| 138 | this.canvas.height = height; |
| 139 | const ctx = this.canvas.getContext('2d'); |
| 140 | if (!ctx) { |
| 141 | return; |
| 142 | } |
| 143 | const pixelRatio = window.devicePixelRatio || 1; |
| 144 | const halfHeight = height / 2; |
| 145 | const barWidth = 2 * pixelRatio; |
| 146 | const barGap = 1 * pixelRatio; |
| 147 | const barIndexScale = width / (barWidth + barGap) / length; |
| 148 | |
| 149 | ctx.beginPath(); |
| 150 | let prevX = 0; |
| 151 | let maxTop = 0; |
| 152 | let maxBottom = 0; |
| 153 | for (let i = 0; i <= length; i++) { |
| 154 | const x = Math.round(i * barIndexScale); |
| 155 | if (x > prevX) { |
| 156 | const topBarHeight = Math.round(maxTop * halfHeight); |
| 157 | const bottomBarHeight = Math.round(maxBottom * halfHeight); |
| 158 | const barHeight = topBarHeight + bottomBarHeight || 1; |
| 159 | ctx.roundRect(prevX * (barWidth + barGap), halfHeight - topBarHeight, barWidth, barHeight, 2); |
| 160 | prevX = x; |
| 161 | maxTop = 0; |
| 162 | maxBottom = 0; |
| 163 | } |
| 164 | const mt = Math.abs(top[i] || 0); |
| 165 | const mb = Math.abs(bottom[i] || 0); |
| 166 | if (mt > maxTop) { |
| 167 | maxTop = mt; |
| 168 | } |
| 169 | if (mb > maxBottom) { |
| 170 | maxBottom = mb; |
| 171 | } |
| 172 | } |
| 173 | ctx.fillStyle = '#436d9d'; |
| 174 | ctx.fill(); |
| 175 | } |