github.com/noriah/catnip@v1.8.5/graphic/display.go (about) 1 package graphic 2 3 import ( 4 "context" 5 "sync/atomic" 6 7 "github.com/noriah/catnip/dsp" 8 "github.com/noriah/catnip/util" 9 10 "github.com/nsf/termbox-go" 11 ) 12 13 // Constants 14 const ( 15 16 // Bar Constants 17 18 SpaceRune = '\u0020' 19 20 BarRuneV = '\u2580' 21 BarRune = '\u2588' 22 BarRuneH = '\u2590' 23 24 StyleReverse = termbox.AttrReverse 25 26 // NumRunes number of runes for sub step bars 27 NumRunes = 8 28 29 // ScalingWindow in seconds 30 ScalingWindow = 1.5 31 // PeakThreshold is the threshold to not draw if the peak is less. 32 PeakThreshold = 0.001 33 ) 34 35 // DrawType is the type. 36 type DrawType int 37 38 // draw types 39 const ( 40 DrawMin DrawType = iota 41 DrawUp 42 DrawUpDown 43 DrawDown 44 DrawLeft 45 DrawLeftRight 46 DrawRight 47 DrawUpDownSplit 48 DrawLeftRightSplit 49 DrawUpDownSplitVert 50 DrawMax 51 52 // DrawDefault is the default draw type. 53 DrawDefault = DrawUpDown 54 ) 55 56 // Styles is the structure for the styles that Display will draw using. 57 type Styles struct { 58 Foreground termbox.Attribute 59 Background termbox.Attribute 60 CenterLine termbox.Attribute 61 } 62 63 // DefaultStyles returns the default styles. 64 func DefaultStyles() Styles { 65 return Styles{ 66 Foreground: termbox.ColorDefault, 67 Background: termbox.ColorDefault, 68 CenterLine: termbox.ColorMagenta, 69 } 70 } 71 72 // StylesFromUInt16 converts 3 uint16 values to styles. 73 func StylesFromUInt16(fg, bg, center uint16) Styles { 74 return Styles{ 75 Foreground: termbox.Attribute(fg), 76 Background: termbox.Attribute(bg), 77 CenterLine: termbox.Attribute(center), 78 } 79 } 80 81 // AsUInt16s converts the styles to 3 uint16 values. 82 func (s Styles) AsUInt16s() (fg, bg, center uint16) { 83 fg = uint16(s.Foreground) 84 bg = uint16(s.Background) 85 center = uint16(s.CenterLine) 86 return 87 } 88 89 // Display handles drawing our visualizer. 90 type Display struct { 91 Smoother dsp.Smoother 92 running uint32 93 barSize int 94 spaceSize int 95 binSize int 96 baseSize int 97 termWidth int 98 termHeight int 99 trackZero int 100 invertDraw bool 101 window *util.MovingWindow 102 drawType DrawType 103 styles Styles 104 styleBuffer []termbox.Attribute 105 ctx context.Context 106 cancel context.CancelFunc 107 } 108 109 func NewDisplay() *Display { 110 return &Display{} 111 } 112 113 // Init initializes the display. 114 // Should be called before any other display method. 115 func (d *Display) Init(sampleRate float64, sampleSize int) error { 116 // make a large buffer as this could be as big as the screen width/height. 117 118 windowSize := ((int(ScalingWindow * sampleRate)) / sampleSize) * 2 119 d.window = util.NewMovingWindow(windowSize) 120 121 d.styleBuffer = make([]termbox.Attribute, 4096) 122 123 // Prevent crash on Tmux. 124 prevState, err := normalizeTerminal() 125 if err != nil { 126 return err 127 } 128 defer prevState() 129 130 if err := termbox.Init(); err != nil { 131 return err 132 } 133 134 termbox.SetInputMode(termbox.InputAlt) 135 termbox.SetOutputMode(termbox.Output256) 136 termbox.HideCursor() 137 138 d.termWidth, d.termHeight = termbox.Size() 139 140 return nil 141 } 142 143 // Close will stop display and clean up the terminal. 144 func (d *Display) Close() error { 145 termbox.Close() 146 return nil 147 } 148 149 // Start display is bad. 150 func (d *Display) Start(ctx context.Context) context.Context { 151 d.ctx, d.cancel = context.WithCancel(ctx) 152 153 go d.inputProcessor() 154 155 return d.ctx 156 } 157 158 // Stop display not work. 159 func (d *Display) Stop() error { 160 if atomic.CompareAndSwapUint32(&d.running, 1, 0) { 161 termbox.Interrupt() 162 } 163 164 return nil 165 } 166 167 // Draw takes data and draws. 168 func (d *Display) Write(buffers [][]float64, channels int) error { 169 170 peak := 0.0 171 bins := d.binsInternal(channels, bufferLength(buffers)) 172 173 for i := 0; i < channels; i++ { 174 for _, val := range buffers[i][:bins] { 175 if val > peak { 176 peak = val 177 } 178 } 179 } 180 181 scale := 1.0 182 183 if peak >= PeakThreshold { 184 d.trackZero = 0 185 186 // do some scaling if we are above the PeakThreshold 187 d.window.Update(peak) 188 189 } else { 190 if d.trackZero++; d.trackZero == 5 { 191 d.window.Recalculate() 192 } 193 } 194 195 vMean, vSD := d.window.Stats() 196 197 if t := vMean + (2.0 * vSD); t > 1.0 { 198 scale = t 199 } 200 201 switch d.drawType { 202 case DrawUp: 203 d.drawUp(buffers, channels, scale) 204 205 case DrawUpDown: 206 d.drawUpDown(buffers, channels, scale) 207 208 case DrawUpDownSplit: 209 d.drawUpDownSplit(buffers, channels, scale) 210 211 case DrawUpDownSplitVert: 212 d.drawUpDownSplitVert(buffers, channels, scale) 213 214 case DrawDown: 215 d.drawDown(buffers, channels, scale) 216 217 case DrawLeft: 218 d.drawLeft(buffers, channels, scale) 219 220 case DrawLeftRight: 221 d.drawLeftRight(buffers, channels, scale) 222 223 case DrawLeftRightSplit: 224 d.drawLeftRightSplit(buffers, channels, scale) 225 226 case DrawRight: 227 d.drawRight(buffers, channels, scale) 228 229 default: 230 return nil 231 } 232 233 termbox.Flush() 234 235 termbox.Clear(d.styles.Foreground, d.styles.Background) 236 237 return nil 238 } 239 240 // SetSizes takes a bar size and spacing size. 241 // Returns number of bars able to show. 242 func (d *Display) SetSizes(bar, space int) { 243 bar = intMax(bar, 1) 244 space = intMax(space, 0) 245 246 d.barSize = bar 247 d.spaceSize = space 248 d.binSize = bar + space 249 } 250 251 // AdjustSizes modifies the bar and space size by barDelta and spaceDelta. 252 func (d *Display) AdjustSizes(barDelta, spaceDelta int) { 253 d.SetSizes(d.barSize+barDelta, d.spaceSize+spaceDelta) 254 } 255 256 // SetBase will set the base size. 257 func (d *Display) SetBase(size int) { 258 size = intMax(size, 0) 259 d.baseSize = size 260 261 d.updateStyleBuffer() 262 } 263 264 // AdjustBase will change the base by delta units 265 func (d *Display) AdjustBase(delta int) { 266 d.SetBase(d.baseSize + delta) 267 } 268 269 func (d *Display) SetStyles(styles Styles) { 270 d.styles = styles 271 272 d.updateStyleBuffer() 273 } 274 275 // SetDrawType sets the draw type for future draws 276 func (d *Display) SetDrawType(dt DrawType) { 277 switch { 278 case dt <= DrawMin: 279 d.drawType = DrawMax - 1 280 case dt >= DrawMax: 281 d.drawType = DrawMin + 1 282 default: 283 d.drawType = dt 284 } 285 286 d.updateStyleBuffer() 287 } 288 289 func (d *Display) SetInvertDraw(invert bool) { 290 d.invertDraw = invert 291 } 292 293 // Bins returns the number of bars we will draw. 294 func (d *Display) Bins(chCount int) int { 295 296 switch d.drawType { 297 case DrawUp, DrawDown: 298 return (d.termWidth / d.binSize) / chCount 299 case DrawUpDownSplit, DrawUpDownSplitVert: 300 return (d.termWidth / d.binSize) / 2 301 case DrawUpDown: 302 return d.termWidth / d.binSize 303 case DrawLeft, DrawRight: 304 return (d.termHeight / d.binSize) / chCount 305 case DrawLeftRightSplit: 306 return (d.termHeight / d.binSize) / 2 307 case DrawLeftRight: 308 return d.termHeight / d.binSize 309 default: 310 return 0 311 } 312 } 313 314 func bufferLength(buffers [][]float64) int { 315 return len(buffers[0]) 316 } 317 318 func (d *Display) binsInternal(chCount, bufLen int) int { 319 bins := d.Bins(chCount) 320 if bins >= bufLen { 321 bins = bufLen - 1 322 } 323 return bins 324 } 325 326 func (d *Display) inputProcessor() { 327 if d.cancel != nil { 328 defer d.cancel() 329 } 330 331 atomic.StoreUint32(&d.running, 1) 332 defer atomic.StoreUint32(&d.running, 0) 333 334 for { 335 ev := termbox.PollEvent() 336 337 switch ev.Type { 338 case termbox.EventKey: 339 switch ev.Key { 340 341 case termbox.KeySpace: 342 d.SetDrawType(d.drawType + 1) 343 344 case termbox.KeyCtrlC: 345 return 346 347 default: 348 switch ev.Ch { 349 case 'b', 'B': 350 d.SetDrawType(d.drawType - 1) 351 352 case 'n', 'N': 353 d.SetDrawType(d.drawType + 1) 354 355 case 'w', 'W': 356 d.AdjustSizes(1, 0) 357 358 case 'd', 'D': 359 d.AdjustSizes(0, 1) 360 361 case 's', 'S': 362 d.AdjustSizes(-1, 0) 363 364 case 'a', 'A': 365 d.AdjustSizes(0, -1) 366 367 case 'i', 'I': 368 d.SetInvertDraw(!d.invertDraw) 369 370 case 'r', 'R': 371 d.window.Drop(d.window.Cap()) 372 373 case '+', '=': 374 d.AdjustBase(1) 375 376 case '-', '_': 377 d.AdjustBase(-1) 378 379 case '[': 380 d.Smoother.SetMethod(d.Smoother.GetMethod() - 1) 381 382 case ']': 383 d.Smoother.SetMethod(d.Smoother.GetMethod() + 1) 384 385 case 'q', 'Q': 386 return 387 388 default: 389 } // switch ev.Ch 390 } // switch ev.Key 391 392 case termbox.EventResize: 393 d.termWidth = ev.Width 394 d.termHeight = ev.Height 395 d.updateStyleBuffer() 396 397 case termbox.EventInterrupt: 398 return 399 400 default: 401 } // switch ev.Type 402 403 // check if we need to exit 404 select { 405 case <-d.ctx.Done(): 406 return 407 default: 408 } 409 } // for 410 } 411 412 func intMax(x1, x2 int) int { 413 if x1 < x2 { 414 return x2 415 } 416 return x1 417 } 418 419 func intMin(x1, x2 int) int { 420 if x1 > x2 { 421 return x2 422 } 423 return x1 424 } 425 426 func (d *Display) updateStyleBuffer() { 427 switch d.drawType { 428 case DrawUp: 429 d.fillStyleBuffer(d.termHeight-d.baseSize, d.baseSize, 0) 430 431 case DrawUpDown, DrawUpDownSplit, DrawUpDownSplitVert: 432 centerStart := intMax((d.termHeight-d.baseSize)/2, 0) 433 centerStop := centerStart + d.baseSize 434 d.fillStyleBuffer(centerStart, d.baseSize, d.termHeight-centerStop) 435 436 case DrawDown: 437 d.fillStyleBuffer(0, d.baseSize, d.termHeight-d.baseSize) 438 439 case DrawLeft: 440 d.fillStyleBuffer(d.termWidth-d.baseSize, d.baseSize, 0) 441 442 case DrawLeftRight, DrawLeftRightSplit: 443 centerStart := intMax((d.termWidth-d.baseSize)/2, 0) 444 centerStop := centerStart + d.baseSize 445 d.fillStyleBuffer(centerStart, d.baseSize, d.termWidth-centerStop) 446 447 case DrawRight: 448 d.fillStyleBuffer(0, d.baseSize, d.termWidth-d.baseSize) 449 } 450 } 451 452 func (d *Display) fillStyleBuffer(left, center, right int) { 453 i := 0 454 for stop := left; i < stop; i++ { 455 d.styleBuffer[i] = d.styles.Foreground 456 } 457 458 for stop := i + center; i < stop; i++ { 459 d.styleBuffer[i] = d.styles.CenterLine 460 } 461 462 for stop := i + right; i < stop; i++ { 463 d.styleBuffer[i] = d.styles.Foreground 464 } 465 } 466 467 func sizeAndCap(value float64, space int, zeroBase bool, baseRune rune) (int, rune) { 468 steps, stop := int(value*NumRunes), space*NumRunes 469 470 if zeroBase { 471 if steps < stop { 472 return space - (steps / NumRunes), baseRune + rune(steps%NumRunes) 473 } 474 475 return 0, baseRune 476 } 477 478 if steps < stop { 479 return steps / NumRunes, baseRune - rune(steps%NumRunes) 480 } 481 482 return space, baseRune 483 } 484 485 // DRAWING METHODS 486 487 // drawUp will draw up. 488 func (d *Display) drawUp(bins [][]float64, channelCount int, scale float64) { 489 binCount := d.binsInternal(channelCount, bufferLength(bins)) 490 barSpace := intMax(d.termHeight-d.baseSize, 0) 491 scale = float64(barSpace) / scale 492 493 paddedWidth := (d.binSize * binCount * channelCount) - d.spaceSize 494 paddedWidth = intMax(intMin(paddedWidth, d.termWidth), 0) 495 496 channelWidth := d.binSize * binCount 497 edgeOffset := (d.termWidth - paddedWidth) / 2 498 499 for xSet, chBins := range bins { 500 501 for xBar := 0; xBar < binCount; xBar++ { 502 503 xBin := (xBar * (1 - xSet)) + (((binCount - 1) - xBar) * xSet) 504 505 if d.invertDraw { 506 xBin = binCount - 1 - xBin 507 } 508 509 start, bCap := sizeAndCap(chBins[xBin]*scale, barSpace, true, BarRuneV) 510 511 xCol := (xBar * d.binSize) + (channelWidth * xSet) + edgeOffset 512 lCol := xCol + d.barSize 513 514 for ; xCol < lCol; xCol++ { 515 516 if bCap > BarRuneV { 517 termbox.SetCell(xCol, start-1, bCap, d.styles.Foreground, d.styles.Background) 518 } 519 520 for xRow := start; xRow < d.termHeight; xRow++ { 521 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xRow], d.styles.Background) 522 } 523 } 524 } 525 } 526 } 527 528 // drawUpDown will draw up and down. 529 func (d *Display) drawUpDown(bins [][]float64, channelCount int, scale float64) { 530 binCount := d.binsInternal(channelCount, bufferLength(bins)) 531 centerStart := intMax((d.termHeight-d.baseSize)/2, 0) 532 centerStop := centerStart + d.baseSize 533 534 scale = float64(intMin(centerStart, d.termHeight-centerStop)) / scale 535 536 edgeOffset := intMax((d.termWidth-((d.binSize*binCount)-d.spaceSize))/2, 0) 537 538 setCount := channelCount 539 540 for xBar := 0; xBar < binCount; xBar++ { 541 542 lStart, lCap := sizeAndCap(bins[0][xBar]*scale, centerStart, true, BarRuneV) 543 rStop, rCap := sizeAndCap(bins[1%setCount][xBar]*scale, centerStart, false, BarRune) 544 if rStop += centerStop; rStop >= d.termHeight { 545 rStop = d.termHeight 546 rCap = BarRune 547 } 548 549 xCol := xBar 550 if d.invertDraw { 551 xCol = binCount - 1 - xCol 552 } 553 554 xCol = xCol*d.binSize + edgeOffset 555 lCol := intMin(xCol+d.barSize, d.termWidth) 556 557 for ; xCol < lCol; xCol++ { 558 559 if lCap > BarRuneV { 560 termbox.SetCell(xCol, lStart-1, lCap, d.styles.Foreground, d.styles.Background) 561 } 562 563 for xRow := lStart; xRow < rStop; xRow++ { 564 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xRow], d.styles.Background) 565 } 566 567 // last part of right bars. 568 if rCap < BarRune { 569 termbox.SetCell(xCol, rStop, rCap, StyleReverse, d.styles.Foreground) 570 } 571 } 572 } 573 } 574 575 // drawUpDownSplit will draw up and down split down the middle for left and 576 // right channels. 577 func (d *Display) drawUpDownSplit(bins [][]float64, channelCount int, scale float64) { 578 binCount := d.binsInternal(2, bufferLength(bins)) 579 centerStart := intMax((d.termHeight-d.baseSize)/2, 0) 580 centerStop := centerStart + d.baseSize 581 582 scale = float64(intMin(centerStart, d.termHeight-centerStop)) / scale 583 584 paddedWidth := (d.binSize * binCount * 2) - d.spaceSize 585 paddedWidth = intMax(intMin(paddedWidth, d.termWidth), 0) 586 587 channelWidth := d.binSize * binCount 588 edgeOffset := (d.termWidth - paddedWidth) / 2 589 590 for xSide := 0; xSide < 2; xSide++ { 591 592 for xBar := 0; xBar < binCount; xBar++ { 593 594 xBin := (xBar * (1 - xSide)) + (((binCount - 1) - xBar) * xSide) 595 596 if d.invertDraw { 597 xBin = binCount - 1 - xBin 598 } 599 600 start, tCap := sizeAndCap(bins[xSide%channelCount][xBin]*scale, centerStart, true, BarRuneV) 601 stop, bCap := sizeAndCap(bins[xSide%channelCount][xBin]*scale, centerStart, false, BarRune) 602 if stop += centerStop; stop >= d.termHeight { 603 stop = d.termHeight 604 bCap = BarRune 605 } 606 607 xCol := (xBar * d.binSize) + (channelWidth * xSide) + edgeOffset 608 lCol := xCol + d.barSize 609 610 for ; xCol < lCol; xCol++ { 611 612 if tCap > BarRuneV { 613 termbox.SetCell(xCol, start-1, tCap, d.styles.Foreground, d.styles.Background) 614 } 615 616 for xRow := start; xRow < stop; xRow++ { 617 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xRow], d.styles.Background) 618 } 619 620 if bCap < BarRune { 621 termbox.SetCell(xCol, stop, bCap, StyleReverse, d.styles.Foreground) 622 } 623 } 624 } 625 } 626 } 627 628 // drawUpDownSplitVert will draw up and down split down the middle for left and 629 // right channels. 630 func (d *Display) drawUpDownSplitVert(bins [][]float64, channelCount int, scale float64) { 631 binCount := d.binsInternal(2, bufferLength(bins)) 632 centerStart := intMax((d.termHeight-d.baseSize)/2, 0) 633 centerStop := centerStart + d.baseSize 634 635 scale = float64(intMin(centerStart, d.termHeight-centerStop)) / scale 636 637 paddedWidth := (d.binSize * binCount * 2) - d.spaceSize 638 paddedWidth = intMax(intMin(paddedWidth, d.termWidth), 0) 639 640 channelWidth := d.binSize * binCount 641 edgeOffset := (d.termWidth - paddedWidth) / 2 642 643 for xSide := 0; xSide < 2; xSide++ { 644 645 for xBar := 0; xBar < binCount; xBar++ { 646 647 xBin := (xBar * (1 - xSide)) + (((binCount - 1) - xBar) * xSide) 648 649 if d.invertDraw { 650 xBin = binCount - 1 - xBin 651 } 652 653 start, tCap := sizeAndCap(bins[0][xBin]*scale, centerStart, true, BarRuneV) 654 stop, bCap := sizeAndCap(bins[1%channelCount][xBin]*scale, centerStart, false, BarRune) 655 if stop += centerStop; stop >= d.termHeight { 656 stop = d.termHeight 657 bCap = BarRune 658 } 659 660 xCol := (xBar * d.binSize) + (channelWidth * xSide) + edgeOffset 661 lCol := xCol + d.barSize 662 663 for ; xCol < lCol; xCol++ { 664 665 if tCap > BarRuneV { 666 termbox.SetCell(xCol, start-1, tCap, d.styles.Foreground, d.styles.Background) 667 } 668 669 for xRow := start; xRow < stop; xRow++ { 670 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xRow], d.styles.Background) 671 } 672 673 if bCap < BarRune { 674 termbox.SetCell(xCol, stop, bCap, StyleReverse, d.styles.Foreground) 675 } 676 } 677 } 678 } 679 } 680 681 // drawDown will draw down. 682 func (d *Display) drawDown(bins [][]float64, channelCount int, scale float64) { 683 binCount := d.binsInternal(channelCount, bufferLength(bins)) 684 barSpace := intMax(d.termHeight-d.baseSize, 0) 685 scale = float64(barSpace) / scale 686 687 paddedWidth := (d.binSize * binCount * channelCount) - d.spaceSize 688 paddedWidth = intMax(intMin(paddedWidth, d.termWidth), 0) 689 690 channelWidth := d.binSize * binCount 691 edgeOffset := (d.termWidth - paddedWidth) / 2 692 693 for xSet, chBins := range bins { 694 695 for xBar := 0; xBar < binCount; xBar++ { 696 697 xBin := (xBar * (1 - xSet)) + (((binCount - 1) - xBar) * xSet) 698 699 if d.invertDraw { 700 xBin = binCount - 1 - xBin 701 } 702 703 stop, bCap := sizeAndCap(chBins[xBin]*scale, barSpace, false, BarRune) 704 if stop += d.baseSize; stop >= d.termHeight { 705 stop = d.termHeight 706 bCap = BarRune 707 } 708 709 xCol := (xBar * d.binSize) + (channelWidth * xSet) + edgeOffset 710 lCol := xCol + d.barSize 711 712 for ; xCol < lCol; xCol++ { 713 714 for xRow := 0; xRow < stop; xRow++ { 715 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xRow], d.styles.Background) 716 } 717 718 if bCap < BarRune { 719 termbox.SetCell(xCol, stop, bCap, StyleReverse, d.styles.Foreground) 720 } 721 } 722 } 723 } 724 } 725 726 func (d *Display) drawLeft(bins [][]float64, channelCount int, scale float64) { 727 binCount := d.binsInternal(channelCount, bufferLength(bins)) 728 barSpace := intMax(d.termWidth-d.baseSize, 0) 729 scale = float64(barSpace) / scale 730 731 paddedWidth := (d.binSize * binCount * channelCount) - d.spaceSize 732 paddedWidth = intMax(intMin(paddedWidth, d.termHeight), 0) 733 734 channelWidth := d.binSize * binCount 735 edgeOffset := (d.termHeight - paddedWidth) / 2 736 737 for xSet, chBins := range bins { 738 739 for xBar := 0; xBar < binCount; xBar++ { 740 741 xBin := (xBar * (1 - xSet)) + (((binCount - 1) - xBar) * xSet) 742 743 if d.invertDraw { 744 xBin = binCount - 1 - xBin 745 } 746 747 start, bCap := sizeAndCap(chBins[xBin]*scale, barSpace, true, BarRune) 748 749 xRow := (xBar * d.binSize) + (channelWidth * xSet) + edgeOffset 750 lRow := xRow + d.barSize 751 752 for ; xRow < lRow; xRow++ { 753 754 if bCap > BarRune { 755 termbox.SetCell(start-1, xRow, bCap, StyleReverse, d.styles.Background) 756 } 757 758 for xCol := start; xCol < d.termWidth; xCol++ { 759 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xCol], d.styles.Background) 760 } 761 } 762 } 763 } 764 } 765 766 // drawLeftRight will draw left and right. 767 func (d *Display) drawLeftRight(bins [][]float64, channelCount int, scale float64) { 768 binCount := d.binsInternal(channelCount, bufferLength(bins)) 769 centerStart := intMax((d.termWidth-d.baseSize)/2, 0) 770 centerStop := centerStart + d.baseSize 771 772 scale = float64(intMin(centerStart, d.termWidth-centerStop)) / scale 773 774 edgeOffset := intMax((d.termHeight-((d.binSize*binCount)-d.spaceSize))/2, 0) 775 776 setCount := channelCount 777 778 for xBar := 0; xBar < binCount; xBar++ { 779 780 // draw higher frequencies at the top 781 xBin := binCount - 1 - xBar 782 783 lStart, lCap := sizeAndCap(bins[0][xBin]*scale, centerStart, true, BarRune) 784 rStop, rCap := sizeAndCap(bins[1%setCount][xBin]*scale, centerStart, false, BarRuneH) 785 if rStop += centerStop; rStop >= d.termWidth { 786 rStop = d.termWidth 787 rCap = BarRuneH 788 } 789 790 xRow := xBar 791 if d.invertDraw { 792 xRow = binCount - 1 - xRow 793 } 794 795 xRow = xRow*d.binSize + edgeOffset 796 797 lRow := intMin(xRow+d.barSize, d.termHeight) 798 799 for ; xRow < lRow; xRow++ { 800 801 if lCap > BarRune { 802 termbox.SetCell(lStart-1, xRow, lCap, StyleReverse, d.styles.Background) 803 } 804 805 for xCol := lStart; xCol < rStop; xCol++ { 806 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xCol], d.styles.Background) 807 } 808 809 if rCap < BarRuneH { 810 termbox.SetCell(rStop, xRow, rCap, d.styles.Foreground, d.styles.Foreground) 811 } 812 } 813 } 814 } 815 816 // drawLeftRight will draw left and right. 817 func (d *Display) drawLeftRightSplit(bins [][]float64, channelCount int, scale float64) { 818 binCount := d.binsInternal(2, bufferLength(bins)) 819 centerStart := intMax((d.termWidth-d.baseSize)/2, 0) 820 centerStop := centerStart + d.baseSize 821 822 scale = float64(intMin(centerStart, d.termWidth-centerStop)) / scale 823 824 paddedWidth := (d.binSize * binCount * 2) - d.spaceSize 825 paddedWidth = intMax(intMin(paddedWidth, d.termHeight), 0) 826 827 channelWidth := d.binSize * binCount 828 edgeOffset := (d.termHeight - paddedWidth) / 2 829 830 for xSide := 0; xSide < 2; xSide++ { 831 832 for xBar := 0; xBar < binCount; xBar++ { 833 834 xBin := (xBar * (1 - xSide)) + (((binCount - 1) - xBar) * xSide) 835 836 if d.invertDraw { 837 xBin = binCount - 1 - xBin 838 } 839 840 start, lCap := sizeAndCap(bins[xSide%channelCount][xBin]*scale, centerStart, true, BarRune) 841 stop, rCap := sizeAndCap(bins[xSide%channelCount][xBin]*scale, centerStart, false, BarRuneH) 842 if stop += centerStop; stop >= d.termWidth { 843 stop = d.termWidth 844 rCap = BarRuneH 845 } 846 847 xRow := (xBar * d.binSize) + (channelWidth * xSide) + edgeOffset 848 lRow := xRow + d.barSize 849 850 for ; xRow < lRow; xRow++ { 851 852 if lCap > BarRune { 853 termbox.SetCell(start-1, xRow, lCap, StyleReverse, d.styles.Background) 854 } 855 856 for xCol := start; xCol < stop; xCol++ { 857 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xCol], d.styles.Background) 858 } 859 860 if rCap < BarRuneH { 861 termbox.SetCell(stop, xRow, rCap, d.styles.Foreground, d.styles.Foreground) 862 } 863 } 864 } 865 } 866 } 867 868 func (d *Display) drawRight(bins [][]float64, channelCount int, scale float64) { 869 binCount := d.binsInternal(channelCount, bufferLength(bins)) 870 barSpace := intMax(d.termWidth-d.baseSize, 0) 871 scale = float64(barSpace) / scale 872 873 paddedWidth := (d.binSize * binCount * channelCount) - d.spaceSize 874 paddedWidth = intMax(intMin(paddedWidth, d.termHeight), 0) 875 876 channelWidth := d.binSize * binCount 877 edgeOffset := (d.termHeight - paddedWidth) / 2 878 879 for xSet, chBins := range bins { 880 881 for xBar := 0; xBar < binCount; xBar++ { 882 883 xBin := (xBar * (1 - xSet)) + (((binCount - 1) - xBar) * xSet) 884 885 if d.invertDraw { 886 xBin = binCount - 1 - xBin 887 } 888 889 stop, bCap := sizeAndCap(chBins[xBin]*scale, barSpace, false, BarRuneH) 890 if stop += d.baseSize; stop >= d.termWidth { 891 stop = d.termWidth 892 bCap = BarRune 893 } 894 895 xRow := (xBar * d.binSize) + (channelWidth * xSet) + edgeOffset 896 lRow := xRow + d.barSize 897 898 for ; xRow < lRow; xRow++ { 899 900 for xCol := 0; xCol < stop; xCol++ { 901 termbox.SetCell(xCol, xRow, BarRune, d.styleBuffer[xCol], d.styles.Background) 902 } 903 904 if bCap < BarRuneH { 905 termbox.SetCell(stop, xRow, bCap, d.styles.Foreground, d.styles.Foreground) 906 } 907 } 908 } 909 } 910 }