1 /* 2 * osclilloscope.cpp 3 * 4 * Created on: 1 Mar 2020 5 * Author: crocoduck 6 */ 7 8 #include <plugins/oscilloscope.h> 9 #include <core/debug.h> 10 #include <core/colors.h> 11 #include <core/util/Color.h> 12 13 #define TRACE_PORT(p) lsp_trace(" port id=%s", (p)->metadata()->id); 14 #define BUF_LIM_SIZE 196608 15 #define PRE_TRG_MAX_SIZE 196608 16 17 #define SWEEP_GEN_N_BITS 32 18 #define SWEEP_GEN_PEAK 1.0f /* Stream min x coordinate should be -SWEEP_GEN_PEAK and max x coordinate should be +SWEEP_GEN_PEAK */ 19 20 #define DC_BLOCK_CUTOFF_HZ 5.0 21 #define DC_BLOCK_DFL_ALPHA 0.999f 22 23 #define STREAM_MAX_X 1.0f 24 #define STREAM_MIN_X -1.0f 25 #define STREAM_MAX_Y 1.0f 26 #define STREAM_MIN_Y -1.0f 27 #define STREAM_N_VER_DIV 4 28 #define STREAM_N_HOR_DIV 4 29 #define DECIM_PRECISION 0.1e-5 /* For development, this should be calculated from screen size */ 30 #define IDISPLAY_DECIM 0.2e-2 /* Decimation for inline display */ 31 32 #define AUTO_SWEEP_TIME 1.0f 33 34 namespace lsp 35 { get_oversampler_mode(size_t portValue)36 over_mode_t oscilloscope_base::get_oversampler_mode(size_t portValue) 37 { 38 switch (portValue) 39 { 40 case oscilloscope_base_metadata::OSC_OVS_NONE: 41 return OM_NONE; 42 case oscilloscope_base_metadata::OSC_OVS_2X: 43 return OM_LANCZOS_2X4; 44 case oscilloscope_base_metadata::OSC_OVS_3X: 45 return OM_LANCZOS_3X4; 46 case oscilloscope_base_metadata::OSC_OVS_4X: 47 return OM_LANCZOS_4X4; 48 case oscilloscope_base_metadata::OSC_OVS_6X: 49 return OM_LANCZOS_6X4; 50 case oscilloscope_base_metadata::OSC_OVS_8X: 51 default: 52 return OM_LANCZOS_8X4; 53 } 54 } 55 get_scope_mode(size_t portValue)56 oscilloscope_base::ch_mode_t oscilloscope_base::get_scope_mode(size_t portValue) 57 { 58 switch (portValue) 59 { 60 case oscilloscope_base_metadata::MODE_XY: 61 return CH_MODE_XY; 62 case oscilloscope_base_metadata::MODE_TRIGGERED: 63 return CH_MODE_TRIGGERED; 64 case oscilloscope_base_metadata::MODE_GONIOMETER: 65 return CH_MODE_GONIOMETER; 66 default: 67 return CH_MODE_DFL; 68 } 69 } 70 get_sweep_type(size_t portValue)71 oscilloscope_base::ch_sweep_type_t oscilloscope_base::get_sweep_type(size_t portValue) 72 { 73 switch (portValue) 74 { 75 case oscilloscope_base_metadata::SWEEP_TYPE_SAWTOOTH: 76 return CH_SWEEP_TYPE_SAWTOOTH; 77 case oscilloscope_base_metadata::SWEEP_TYPE_TRIANGULAR: 78 return CH_SWEEP_TYPE_TRIANGULAR; 79 case oscilloscope_base_metadata::SWEEP_TYPE_SINE: 80 return CH_SWEEP_TYPE_SINE; 81 default: 82 return CH_SWEEP_TYPE_DFL; 83 } 84 } 85 get_trigger_input(size_t portValue)86 oscilloscope_base::ch_trg_input_t oscilloscope_base::get_trigger_input(size_t portValue) 87 { 88 switch (portValue) 89 { 90 case oscilloscope_base_metadata::TRIGGER_INPUT_Y: 91 return CH_TRG_INPUT_Y; 92 case oscilloscope_base_metadata::TRIGGER_INPUT_EXT: 93 return CH_TRG_INPUT_EXT; 94 default: 95 return CH_TRG_INPUT_DFL; 96 } 97 } 98 get_coupling_type(size_t portValue)99 oscilloscope_base::ch_coupling_t oscilloscope_base::get_coupling_type(size_t portValue) 100 { 101 switch (portValue) 102 { 103 case oscilloscope_base_metadata::COUPLING_AC: 104 return CH_COUPLING_AC; 105 case oscilloscope_base_metadata::COUPLING_DC: 106 return CH_COUPLING_DC; 107 default: 108 return CH_COUPLING_DFL; 109 110 } 111 } 112 get_trigger_mode(size_t portValue)113 trg_mode_t oscilloscope_base::get_trigger_mode(size_t portValue) 114 { 115 switch (portValue) 116 { 117 case oscilloscope_base_metadata::TRIGGER_MODE_SINGLE: 118 return TRG_MODE_SINGLE; 119 case oscilloscope_base_metadata::TRIGGER_MODE_MANUAL: 120 return TRG_MODE_MANUAL; 121 case oscilloscope_base_metadata::TRIGGER_MODE_REPEAT: 122 return TRG_MODE_REPEAT; 123 default: 124 return TRG_MODE_REPEAT; 125 } 126 } 127 get_trigger_type(size_t portValue)128 trg_type_t oscilloscope_base::get_trigger_type(size_t portValue) 129 { 130 switch (portValue) 131 { 132 case oscilloscope_base_metadata::TRIGGER_TYPE_NONE: 133 return TRG_TYPE_NONE; 134 case oscilloscope_base_metadata::TRIGGER_TYPE_SIMPLE_RISING_EDGE: 135 return TRG_TYPE_SIMPLE_RISING_EDGE; 136 case oscilloscope_base_metadata::TRIGGER_TYPE_SIMPE_FALLING_EDGE: 137 return TRG_TYPE_SIMPLE_FALLING_EDGE; 138 case oscilloscope_base_metadata::TRIGGER_TYPE_ADVANCED_RISING_EDGE: 139 return TRG_TYPE_ADVANCED_RISING_EDGE; 140 case oscilloscope_base_metadata::TRIGGER_TYPE_ADVANCED_FALLING_EDGE: 141 return TRG_TYPE_ADVANCED_FALLING_EDGE; 142 default: 143 return TRG_TYPE_NONE; 144 } 145 } 146 update_dc_block_filter(FilterBank & rFilterBank)147 void oscilloscope_base::update_dc_block_filter(FilterBank &rFilterBank) 148 { 149 /* Filter Transfer Function: 150 * 151 * g - g z^-1 152 * H(z) = ---------------- 153 * 1 - a * z^-1 154 * 155 * With g = sDCBlockParams.fGain, a = sACBlockParams.fAlpha 156 */ 157 158 rFilterBank.begin(); 159 160 biquad_x1_t *f = rFilterBank.add_chain(); 161 if (f == NULL) 162 return; 163 164 f->b0 = sDCBlockParams.fGain; 165 f->b1 = -sDCBlockParams.fGain; 166 f->b2 = 0.0f; 167 f->a1 = sDCBlockParams.fAlpha; 168 f->a2 = 0.0f; 169 f->p0 = 0.0f; 170 f->p1 = 0.0f; 171 f->p2 = 0.0f; 172 173 rFilterBank.end(true); 174 } 175 reconfigure_dc_block_filters()176 void oscilloscope_base::reconfigure_dc_block_filters() 177 { 178 double omega = 2.0 * M_PI * DC_BLOCK_CUTOFF_HZ / fSampleRate; // Normalised frequency 179 180 double c = cos(omega); 181 double g = 1.9952623149688795; // This is 10^(3/10), used to calculate the parameter alpha so that it is exactly associated to the cutoff frequency (-3 dB). 182 double r = sqrt(c*c - 1.0 - 2.0 * g * c + 2.0 * g); 183 184 double alpha1 = c + r; 185 double alpha2 = c - r; 186 187 if ((alpha1 >= 0.0) && (alpha1 < 1.0)) 188 sDCBlockParams.fAlpha = alpha1; 189 else if ((alpha2 >= 0.0) && (alpha2 < 1.0)) 190 sDCBlockParams.fAlpha = alpha2; 191 else 192 sDCBlockParams.fAlpha = DC_BLOCK_DFL_ALPHA; 193 194 sDCBlockParams.fGain = 0.5f * (1.0f + sDCBlockParams.fAlpha); 195 196 for (size_t ch = 0; ch < nChannels; ++ch) 197 { 198 channel_t *c = &vChannels[ch]; 199 200 update_dc_block_filter(c->sDCBlockBank_x); 201 update_dc_block_filter(c->sDCBlockBank_y); 202 update_dc_block_filter(c->sDCBlockBank_ext); 203 } 204 } 205 do_sweep_step(channel_t * c,float strobe_value)206 void oscilloscope_base::do_sweep_step(channel_t *c, float strobe_value) 207 { 208 c->sSweepGenerator.process_overwrite(&c->vDisplay_x[c->nDisplayHead], 1); 209 c->vDisplay_y[c->nDisplayHead] = c->vData_y_delay[c->nDataHead]; 210 c->vDisplay_s[c->nDisplayHead] = strobe_value; 211 ++c->nDataHead; 212 ++c->nDisplayHead; 213 } 214 select_trigger_input(float * extPtr,float * yPtr,ch_trg_input_t input)215 float *oscilloscope_base::select_trigger_input(float *extPtr, float* yPtr, ch_trg_input_t input) 216 { 217 switch (input) 218 { 219 case CH_TRG_INPUT_EXT: 220 return extPtr; 221 222 case CH_TRG_INPUT_Y: 223 default: 224 return yPtr; 225 } 226 } 227 set_oversampler(Oversampler & over,over_mode_t mode)228 void oscilloscope_base::set_oversampler(Oversampler &over, over_mode_t mode) 229 { 230 over.set_mode(mode); 231 if (over.modified()) 232 over.update_settings(); 233 } 234 set_sweep_generator(channel_t * c)235 void oscilloscope_base::set_sweep_generator(channel_t *c) 236 { 237 c->sSweepGenerator.set_sample_rate(c->nOverSampleRate); 238 c->sSweepGenerator.set_frequency(c->nOverSampleRate / c->nSweepSize); 239 240 switch (c->enSweepType) 241 { 242 case CH_SWEEP_TYPE_TRIANGULAR: 243 { 244 c->sSweepGenerator.set_function(FG_SAWTOOTH); 245 c->sSweepGenerator.set_dc_reference(DC_WAVEDC); 246 c->sSweepGenerator.set_amplitude(SWEEP_GEN_PEAK); 247 c->sSweepGenerator.set_dc_offset(0.0f); 248 c->sSweepGenerator.set_width(0.5f); 249 } 250 break; 251 252 case CH_SWEEP_TYPE_SINE: 253 { 254 c->sSweepGenerator.set_function(FG_SINE); 255 c->sSweepGenerator.set_dc_reference(DC_WAVEDC); 256 c->sSweepGenerator.set_amplitude(SWEEP_GEN_PEAK); 257 c->sSweepGenerator.set_dc_offset(0.0f); 258 } 259 break; 260 261 case CH_SWEEP_TYPE_SAWTOOTH: 262 default: 263 { 264 c->sSweepGenerator.set_function(FG_SAWTOOTH); 265 c->sSweepGenerator.set_dc_reference(DC_WAVEDC); 266 c->sSweepGenerator.set_amplitude(SWEEP_GEN_PEAK); 267 c->sSweepGenerator.set_dc_offset(0.0f); 268 c->sSweepGenerator.set_width(1.0f); 269 } 270 break; 271 } 272 273 c->sSweepGenerator.update_settings(); 274 } 275 configure_oversamplers(channel_t * c,over_mode_t mode)276 void oscilloscope_base::configure_oversamplers(channel_t *c, over_mode_t mode) 277 { 278 c->enOverMode = mode; 279 280 set_oversampler(c->sOversampler_x, c->enOverMode); 281 set_oversampler(c->sOversampler_y, c->enOverMode); 282 set_oversampler(c->sOversampler_ext, c->enOverMode); 283 284 // All are set the same way, use any to get these variables 285 c->nOversampling = c->sOversampler_x.get_oversampling(); 286 c->nOverSampleRate = c->nOversampling * fSampleRate; 287 } 288 oscilloscope_base(const plugin_metadata_t & metadata,size_t channels)289 oscilloscope_base::oscilloscope_base(const plugin_metadata_t &metadata, size_t channels): plugin_t(metadata) 290 { 291 nChannels = channels; 292 vChannels = NULL; 293 294 pData = NULL; 295 296 pStrobeHistSize = NULL; 297 pXYRecordTime = NULL; 298 pFreeze = NULL; 299 300 pChannelSelector = NULL; 301 302 pOvsMode = NULL; 303 pScpMode = NULL; 304 pCoupling_x = NULL; 305 pCoupling_y = NULL; 306 pCoupling_ext = NULL; 307 308 pSweepType = NULL; 309 pTimeDiv = NULL; 310 pHorDiv = NULL; 311 pHorPos = NULL; 312 313 pVerDiv = NULL; 314 pVerPos = NULL; 315 316 pTrgHys = NULL; 317 pTrgLev = NULL; 318 pTrgHold = NULL; 319 pTrgMode = NULL; 320 pTrgType = NULL; 321 pTrgInput = NULL; 322 pTrgReset = NULL; 323 324 pIDisplay = NULL; 325 } 326 ~oscilloscope_base()327 oscilloscope_base::~oscilloscope_base() 328 { 329 } 330 destroy()331 void oscilloscope_base::destroy() 332 { 333 free_aligned(pData); 334 pData = NULL; 335 336 if (vChannels != NULL) 337 { 338 for (size_t ch = 0; ch < nChannels; ++ch) 339 { 340 channel_t *c = &vChannels[ch]; 341 342 c->sDCBlockBank_x.destroy(); 343 c->sDCBlockBank_y.destroy(); 344 c->sDCBlockBank_ext.destroy(); 345 346 c->sOversampler_x.destroy(); 347 c->sOversampler_y.destroy(); 348 c->sOversampler_ext.destroy(); 349 350 c->sPreTrgDelay.destroy(); 351 352 c->sSweepGenerator.destroy(); 353 354 c->vTemp = NULL; 355 c->vData_x = NULL; 356 c->vData_y = NULL; 357 c->vData_ext = NULL; 358 c->vData_y_delay = NULL; 359 c->vDisplay_x = NULL; 360 c->vDisplay_y = NULL; 361 c->vDisplay_s = NULL; 362 363 c->vIDisplay_x = NULL; 364 c->vIDisplay_y = NULL; 365 } 366 367 delete [] vChannels; 368 vChannels = NULL; 369 } 370 371 if (pIDisplay != NULL) 372 { 373 pIDisplay->detroy(); 374 pIDisplay = NULL; 375 } 376 } 377 init(IWrapper * wrapper)378 void oscilloscope_base::init(IWrapper *wrapper) 379 { 380 plugin_t::init(wrapper); 381 382 vChannels = new channel_t[nChannels]; 383 if (vChannels == NULL) 384 return; 385 386 /** For each channel: 387 * 1X temp buffer + 388 * 1X external data buffer + 389 * 1X x data buffer + 390 * 1X y data buffer + 391 * 1X delayed y data buffer + 392 * 1X x display buffer + 393 * 1X y display buffer + 394 * 1X strobe display buffer 395 * 1X x inline display buffer 396 * 1X y inline display buffer 397 * 398 * All buffers size BUF_LIM_SIZE 399 */ 400 size_t samples = nChannels * BUF_LIM_SIZE * 10; 401 402 float *ptr = alloc_aligned<float>(pData, samples); 403 if (ptr == NULL) 404 return; 405 406 lsp_guard_assert(float *save = ptr); 407 408 for (size_t ch = 0; ch < nChannels; ++ch) 409 { 410 channel_t *c = &vChannels[ch]; 411 412 init_state_stage(c); 413 414 if (!c->sDCBlockBank_x.init(FILTER_CHAINS_MAX)) 415 return; 416 417 if (!c->sDCBlockBank_y.init(FILTER_CHAINS_MAX)) 418 return; 419 420 if (!c->sDCBlockBank_ext.init(FILTER_CHAINS_MAX)) 421 return; 422 423 if (!c->sOversampler_x.init()) 424 return; 425 426 if (!c->sOversampler_y.init()) 427 return; 428 429 if (!c->sOversampler_ext.init()) 430 return; 431 432 if (!c->sPreTrgDelay.init(PRE_TRG_MAX_SIZE)) 433 return; 434 435 // Settings for the Sweep Generator 436 c->sSweepGenerator.init(); 437 c->sSweepGenerator.set_phase_accumulator_bits(SWEEP_GEN_N_BITS); 438 c->sSweepGenerator.set_phase(0.0f); 439 c->sSweepGenerator.update_settings(); 440 441 c->vTemp = ptr; 442 ptr += BUF_LIM_SIZE; 443 444 c->vData_x = ptr; 445 ptr += BUF_LIM_SIZE; 446 447 c->vData_y = ptr; 448 ptr += BUF_LIM_SIZE; 449 450 c->vData_ext = ptr; 451 ptr += BUF_LIM_SIZE; 452 453 c->vData_y_delay = ptr; 454 ptr += BUF_LIM_SIZE; 455 456 c->vDisplay_x = ptr; 457 ptr += BUF_LIM_SIZE; 458 459 c->vDisplay_y = ptr; 460 ptr += BUF_LIM_SIZE; 461 462 c->vDisplay_s = ptr; 463 ptr += BUF_LIM_SIZE; 464 465 c->vIDisplay_x = ptr; 466 ptr += BUF_LIM_SIZE; 467 468 c->vIDisplay_y = ptr; 469 ptr += BUF_LIM_SIZE; 470 471 c->nIDisplay = 0; 472 473 c->nDataHead = 0; 474 c->nDisplayHead = 0; 475 c->nSamplesCounter = 0; 476 c->bClearStream = false; 477 478 c->nPreTrigger = 0; 479 c->nSweepSize = 0; 480 481 c->fVerStreamScale = 0.0f; 482 c->fVerStreamOffset = 0.0f; 483 484 c->bAutoSweep = true; 485 c->nAutoSweepLimit = 0; 486 c->nAutoSweepCounter = 0; 487 488 c->enState = CH_STATE_LISTENING; 489 490 c->vIn_x = NULL; 491 c->vIn_y = NULL; 492 c->vIn_ext = NULL; 493 494 c->vOut_x = NULL; 495 c->vOut_y = NULL; 496 497 c->pIn_x = NULL; 498 c->pIn_y = NULL; 499 c->pIn_ext = NULL; 500 501 c->pOut_x = NULL; 502 c->pOut_y = NULL; 503 504 c->pOvsMode = NULL; 505 c->pScpMode = NULL; 506 c->pCoupling_x = NULL; 507 c->pCoupling_y = NULL; 508 c->pCoupling_ext = NULL; 509 510 c->pSweepType = NULL; 511 c->pTimeDiv = NULL; 512 c->pHorPos = NULL; 513 514 c->pVerDiv = NULL; 515 c->pVerPos = NULL; 516 517 c->pTrgHys = NULL; 518 c->pTrgLev = NULL; 519 c->pTrgHold = NULL; 520 c->pTrgMode = NULL; 521 c->pTrgType = NULL; 522 c->pTrgInput = NULL; 523 c->pTrgReset = NULL; 524 525 c->pGlobalSwitch = NULL; 526 c->pFreezeSwitch = NULL; 527 c->pSoloSwitch = NULL; 528 c->pMuteSwitch = NULL; 529 530 c->pStream = NULL; 531 } 532 533 lsp_assert(ptr <= &save[samples]); 534 535 // Bind ports 536 size_t port_id = 0; 537 538 lsp_trace("Binding audio ports"); 539 540 for (size_t ch = 0; ch < nChannels; ++ch) 541 { 542 TRACE_PORT(vPorts[port_id]); 543 vChannels[ch].pIn_x = vPorts[port_id++]; 544 545 TRACE_PORT(vPorts[port_id]); 546 vChannels[ch].pIn_y = vPorts[port_id++]; 547 548 TRACE_PORT(vPorts[port_id]); 549 vChannels[ch].pIn_ext = vPorts[port_id++]; 550 551 TRACE_PORT(vPorts[port_id]); 552 vChannels[ch].pOut_x = vPorts[port_id++]; 553 554 TRACE_PORT(vPorts[port_id]); 555 vChannels[ch].pOut_y = vPorts[port_id++]; 556 } 557 558 // Common settings 559 lsp_trace("Binding common ports"); 560 561 TRACE_PORT(vPorts[port_id]); 562 pStrobeHistSize = vPorts[port_id++]; 563 564 TRACE_PORT(vPorts[port_id]); 565 pXYRecordTime = vPorts[port_id++]; 566 567 TRACE_PORT(vPorts[port_id]); 568 ++port_id; // Skip 'maxdots' parameter 569 570 TRACE_PORT(vPorts[port_id]); 571 pFreeze = vPorts[port_id++]; 572 573 // Channel selector only exists on multi-channel versions. Skip for 1X plugin. 574 if (nChannels > 1) 575 { 576 TRACE_PORT(vPorts[port_id]); 577 pChannelSelector = vPorts[port_id++]; 578 } 579 580 // Global ports only exists on multi-channel versions. Skip for 1X plugin. 581 582 if (nChannels > 1) 583 { 584 lsp_trace("Binding global control ports"); 585 586 TRACE_PORT(vPorts[port_id]); 587 pOvsMode = vPorts[port_id++]; 588 589 TRACE_PORT(vPorts[port_id]); 590 pScpMode = vPorts[port_id++]; 591 592 TRACE_PORT(vPorts[port_id]); 593 pCoupling_x = vPorts[port_id++]; 594 595 TRACE_PORT(vPorts[port_id]); 596 pCoupling_y = vPorts[port_id++]; 597 598 TRACE_PORT(vPorts[port_id]); 599 pCoupling_ext = vPorts[port_id++]; 600 601 TRACE_PORT(vPorts[port_id]); 602 pSweepType = vPorts[port_id++]; 603 604 TRACE_PORT(vPorts[port_id]); 605 pTimeDiv = vPorts[port_id++]; 606 607 TRACE_PORT(vPorts[port_id]); 608 pHorDiv = vPorts[port_id++]; 609 610 TRACE_PORT(vPorts[port_id]); 611 pHorPos = vPorts[port_id++]; 612 613 TRACE_PORT(vPorts[port_id]); 614 pVerDiv = vPorts[port_id++]; 615 616 TRACE_PORT(vPorts[port_id]); 617 pVerPos = vPorts[port_id++]; 618 619 TRACE_PORT(vPorts[port_id]); 620 pTrgHys = vPorts[port_id++]; 621 622 TRACE_PORT(vPorts[port_id]); 623 pTrgLev = vPorts[port_id++]; 624 625 TRACE_PORT(vPorts[port_id]); 626 pTrgHold = vPorts[port_id++]; 627 628 TRACE_PORT(vPorts[port_id]); 629 pTrgMode = vPorts[port_id++]; 630 631 TRACE_PORT(vPorts[port_id]); 632 pTrgType = vPorts[port_id++]; 633 634 TRACE_PORT(vPorts[port_id]); 635 pTrgInput = vPorts[port_id++]; 636 637 TRACE_PORT(vPorts[port_id]); 638 pTrgReset = vPorts[port_id++]; 639 } 640 641 lsp_trace("Binding channel control ports"); 642 643 644 for (size_t ch = 0; ch < nChannels; ++ch) 645 { 646 TRACE_PORT(vPorts[port_id]); 647 vChannels[ch].pOvsMode = vPorts[port_id++]; 648 649 TRACE_PORT(vPorts[port_id]); 650 vChannels[ch].pScpMode = vPorts[port_id++]; 651 652 TRACE_PORT(vPorts[port_id]); 653 vChannels[ch].pCoupling_x = vPorts[port_id++]; 654 655 TRACE_PORT(vPorts[port_id]); 656 vChannels[ch].pCoupling_y = vPorts[port_id++]; 657 658 TRACE_PORT(vPorts[port_id]); 659 vChannels[ch].pCoupling_ext = vPorts[port_id++]; 660 661 TRACE_PORT(vPorts[port_id]); 662 vChannels[ch].pSweepType = vPorts[port_id++]; 663 664 TRACE_PORT(vPorts[port_id]); 665 vChannels[ch].pTimeDiv = vPorts[port_id++]; 666 667 TRACE_PORT(vPorts[port_id]); 668 vChannels[ch].pHorDiv = vPorts[port_id++]; 669 670 TRACE_PORT(vPorts[port_id]); 671 vChannels[ch].pHorPos = vPorts[port_id++]; 672 673 TRACE_PORT(vPorts[port_id]); 674 vChannels[ch].pVerDiv = vPorts[port_id++]; 675 676 TRACE_PORT(vPorts[port_id]); 677 vChannels[ch].pVerPos = vPorts[port_id++]; 678 679 TRACE_PORT(vPorts[port_id]); 680 vChannels[ch].pTrgHys = vPorts[port_id++]; 681 682 TRACE_PORT(vPorts[port_id]); 683 vChannels[ch].pTrgLev = vPorts[port_id++]; 684 685 TRACE_PORT(vPorts[port_id]); 686 vChannels[ch].pTrgHold = vPorts[port_id++]; 687 688 TRACE_PORT(vPorts[port_id]); 689 vChannels[ch].pTrgMode = vPorts[port_id++]; 690 691 TRACE_PORT(vPorts[port_id]); 692 vChannels[ch].pTrgType = vPorts[port_id++]; 693 694 TRACE_PORT(vPorts[port_id]); 695 vChannels[ch].pTrgInput = vPorts[port_id++]; 696 697 TRACE_PORT(vPorts[port_id]); 698 vChannels[ch].pTrgReset = vPorts[port_id++]; 699 } 700 701 lsp_trace("Binding channel switches ports"); 702 703 if (nChannels > 1) 704 { 705 for (size_t ch = 0; ch < nChannels; ++ch) 706 { 707 TRACE_PORT(vPorts[port_id]); 708 vChannels[ch].pGlobalSwitch = vPorts[port_id++]; 709 710 TRACE_PORT(vPorts[port_id]); 711 vChannels[ch].pFreezeSwitch = vPorts[port_id++]; 712 713 TRACE_PORT(vPorts[port_id]); 714 vChannels[ch].pSoloSwitch = vPorts[port_id++]; 715 716 TRACE_PORT(vPorts[port_id]); 717 vChannels[ch].pMuteSwitch = vPorts[port_id++]; 718 } 719 } 720 721 lsp_trace("Binding channel visual outputs ports"); 722 723 for (size_t ch = 0; ch < nChannels; ++ch) 724 { 725 TRACE_PORT(vPorts[port_id]); 726 vChannels[ch].pStream = vPorts[port_id++]; 727 } 728 } 729 init_state_stage(channel_t * c)730 void oscilloscope_base::init_state_stage(channel_t *c) 731 { 732 c->nUpdate = 0; 733 734 c->sStateStage.nPV_pScpMode = oscilloscope_base_metadata::MODE_DFL; 735 c->nUpdate |= UPD_SCPMODE; 736 737 c->sStateStage.nPV_pCoupling_x = oscilloscope_base_metadata::COUPLING_DFL; 738 c->nUpdate |= UPD_ACBLOCK_X; 739 740 c->sStateStage.nPV_pCoupling_y = oscilloscope_base_metadata::COUPLING_DFL; 741 c->nUpdate |= UPD_ACBLOCK_Y; 742 743 c->sStateStage.nPV_pCoupling_ext = oscilloscope_base_metadata::COUPLING_DFL; 744 c->nUpdate |= UPD_ACBLOCK_EXT; 745 746 c->sStateStage.nPV_pOvsMode = oscilloscope_base_metadata::OSC_OVS_DFL; 747 c->nUpdate |= UPD_OVERSAMPLER_X; 748 c->nUpdate |= UPD_OVERSAMPLER_Y; 749 c->nUpdate |= UPD_OVERSAMPLER_EXT; 750 751 c->sStateStage.nPV_pTrgInput = oscilloscope_base_metadata::TRIGGER_INPUT_DFL; 752 c->nUpdate |= UPD_TRIGGER_INPUT; 753 754 c->sStateStage.fPV_pVerDiv = oscilloscope_base_metadata::VERTICAL_DIVISION_DFL; 755 c->sStateStage.fPV_pVerPos = oscilloscope_base_metadata::VERTICAL_POSITION_DFL; 756 c->nUpdate |= UPD_TRIGGER; 757 758 c->sStateStage.fPV_pTrgHys = oscilloscope_base_metadata::TRIGGER_HYSTERESIS_DFL; 759 c->nUpdate |= UPD_TRIGGER; 760 761 c->sStateStage.fPV_pTrgLevel = oscilloscope_base_metadata::TRIGGER_LEVEL_DFL; 762 c->nUpdate |= UPD_TRIGGER; 763 764 c->sStateStage.nPV_pTrgMode = oscilloscope_base_metadata::TRIGGER_MODE_DFL; 765 c->nUpdate |= UPD_TRIGGER; 766 767 c->sStateStage.fPV_pTrgHold = oscilloscope_base_metadata::TRIGGER_HOLD_TIME_DFL; 768 c->nUpdate |= UPD_TRIGGER_HOLD; 769 770 c->sStateStage.nPV_pTrgType = oscilloscope_base_metadata::TRIGGER_TYPE_DFL; 771 c->nUpdate |= UPD_TRIGGER; 772 773 c->sStateStage.fPV_pTimeDiv = oscilloscope_base_metadata::TIME_DIVISION_DFL; 774 c->nUpdate |= UPD_SWEEP_GENERATOR; 775 776 c->sStateStage.fPV_pHorPos = oscilloscope_base_metadata::TIME_POSITION_DFL; 777 c->nUpdate |= UPD_SWEEP_GENERATOR; 778 c->nUpdate |= UPD_PRETRG_DELAY; 779 780 c->sStateStage.nPV_pSweepType = oscilloscope_base_metadata::SWEEP_TYPE_DFL; 781 c->nUpdate |= UPD_SWEEP_GENERATOR; 782 783 c->sStateStage.fPV_pXYRecordTime = oscilloscope_base_metadata::XY_RECORD_TIME_DFL; 784 c->nUpdate |= UPD_XY_RECORD_TIME; 785 786 c->nUpdate |= UPD_VER_SCALES; 787 c->nUpdate |= UPD_HOR_SCALES; 788 789 // By default, this must be false. 790 c->bUseGlobal = false; 791 c->bFreeze = false; 792 c->bVisible = false; 793 } 794 commit_staged_state_change(channel_t * c)795 void oscilloscope_base::commit_staged_state_change(channel_t *c) 796 { 797 if (c->nUpdate == 0) 798 return; 799 800 if (c->nUpdate & UPD_SCPMODE) 801 { 802 c->enMode = get_scope_mode(c->sStateStage.nPV_pScpMode); 803 c->nDisplayHead = 0; // Reset the display head 804 } 805 806 if (c->nUpdate & UPD_ACBLOCK_X) 807 c->enCoupling_x = get_coupling_type(c->sStateStage.nPV_pCoupling_x); 808 809 if (c->nUpdate & UPD_ACBLOCK_Y) 810 c->enCoupling_y = get_coupling_type(c->sStateStage.nPV_pCoupling_y); 811 812 if (c->nUpdate & UPD_ACBLOCK_EXT) 813 c->enCoupling_ext = get_coupling_type(c->sStateStage.nPV_pCoupling_ext); 814 815 if (c->nUpdate & (UPD_OVERSAMPLER_X | UPD_OVERSAMPLER_Y | UPD_OVERSAMPLER_EXT)) 816 configure_oversamplers(c, get_oversampler_mode(c->sStateStage.nPV_pOvsMode)); 817 818 if (c->nUpdate & UPD_XY_RECORD_TIME) 819 { 820 c->nXYRecordSize = millis_to_samples(c->nOverSampleRate, c->sStateStage.fPV_pXYRecordTime); 821 c->nXYRecordSize = (c->nXYRecordSize < BUF_LIM_SIZE) ? c->nXYRecordSize : BUF_LIM_SIZE; 822 } 823 824 // UPD_SWEEP_GENERATOR handling is split because if also UPD_PRETRG_DELAY needs to be handled them the correct order of operations is as follows. 825 if (c->nUpdate & UPD_SWEEP_GENERATOR) 826 { 827 c->nSweepSize = STREAM_N_HOR_DIV * millis_to_samples(c->nOverSampleRate, c->sStateStage.fPV_pTimeDiv); 828 c->nSweepSize = (c->nSweepSize < BUF_LIM_SIZE) ? c->nSweepSize : BUF_LIM_SIZE; 829 } 830 831 if (c->nUpdate & UPD_PRETRG_DELAY) 832 { 833 c->nPreTrigger = 0.5f * (0.01f * c->sStateStage.fPV_pHorPos + 1) * (c->nSweepSize - 1); 834 c->nPreTrigger = (c->nPreTrigger < PRE_TRG_MAX_SIZE) ? c->nPreTrigger : PRE_TRG_MAX_SIZE; 835 c->sPreTrgDelay.set_delay(c->nPreTrigger); 836 c->sPreTrgDelay.clear(); 837 } 838 839 if (c->nUpdate & UPD_SWEEP_GENERATOR) 840 { 841 c->enSweepType = get_sweep_type(c->sStateStage.nPV_pSweepType); 842 set_sweep_generator(c); 843 844 // Since the seep period has changed, we need to revert state to LISTENING. 845 c->enState = CH_STATE_LISTENING; 846 } 847 848 if (c->nUpdate & UPD_TRIGGER_INPUT) 849 c->enTrgInput = get_trigger_input(c->sStateStage.nPV_pTrgInput); 850 851 if (c->nUpdate & UPD_TRIGGER_HOLD) 852 { 853 size_t minHold = c->nSweepSize; 854 size_t trgHold = seconds_to_samples(c->nOverSampleRate, c->sStateStage.fPV_pTrgHold); 855 trgHold = trgHold > minHold ? trgHold : minHold; 856 c->sTrigger.set_trigger_hold_samples(trgHold); 857 858 c->nAutoSweepLimit = seconds_to_samples(c->nOverSampleRate, AUTO_SWEEP_TIME); 859 c->nAutoSweepLimit = (c->nAutoSweepLimit < trgHold) ? trgHold: c->nAutoSweepLimit; 860 c->nAutoSweepCounter = 0; 861 } 862 863 if (c->nUpdate & UPD_HOR_SCALES) 864 { 865 c->fHorStreamScale = (STREAM_MAX_X - STREAM_MIN_X) / (STREAM_N_HOR_DIV * c->sStateStage.fPV_pHorDiv); 866 c->fHorStreamOffset = 0.5f * (STREAM_MAX_X - STREAM_MIN_X) * (0.01f * c->sStateStage.fPV_pHorPos + 1.0f) + STREAM_MIN_X; 867 } 868 869 if (c->nUpdate & UPD_VER_SCALES) 870 { 871 c->fVerStreamScale = (STREAM_MAX_Y - STREAM_MIN_Y) / (STREAM_N_VER_DIV * c->sStateStage.fPV_pVerDiv); 872 c->fVerStreamOffset = 0.5f * (STREAM_MAX_Y - STREAM_MIN_Y) * (0.01f * c->sStateStage.fPV_pVerPos + 1.0f) + STREAM_MIN_Y; 873 } 874 875 if (c->nUpdate & UPD_TRIGGER) 876 { 877 trg_mode_t trgMode = get_trigger_mode(c->sStateStage.nPV_pTrgMode); 878 879 c->bAutoSweep = !((trgMode == TRG_MODE_SINGLE) || (trgMode == TRG_MODE_MANUAL)); 880 c->sTrigger.set_trigger_mode(trgMode); 881 c->sTrigger.set_trigger_hysteresis(0.01f * c->sStateStage.fPV_pTrgHys * STREAM_N_VER_DIV * c->sStateStage.fPV_pVerDiv); 882 c->sTrigger.set_trigger_type(get_trigger_type(c->sStateStage.nPV_pTrgType)); 883 c->sTrigger.set_trigger_threshold(0.5f * STREAM_N_VER_DIV * c->sStateStage.fPV_pVerDiv * 0.01f * c->sStateStage.fPV_pTrgLevel); 884 c->sTrigger.update_settings(); 885 } 886 887 if (c->nUpdate & UPD_TRGGER_RESET) 888 { 889 c->sTrigger.reset_single_trigger(); 890 c->sTrigger.activate_manual_trigger(); 891 } 892 893 c->bClearStream = true; 894 895 // Clear the update flag 896 c->nUpdate = 0; 897 } 898 graph_stream(channel_t * c)899 bool oscilloscope_base::graph_stream(channel_t * c) 900 { 901 // Remember size and reset head 902 size_t query_size = c->nDisplayHead; 903 c->nDisplayHead = 0; 904 905 // Check that stream is present 906 stream_t *stream = c->pStream->getBuffer<stream_t>(); 907 if ((stream == NULL) || (c->bFreeze)) 908 return false; 909 910 if (c->bClearStream) 911 { 912 stream->clear(); 913 c->bClearStream = false; 914 } 915 916 // Transform XY -> MS for goniomteter mode 917 if (c->enMode == CH_MODE_GONIOMETER) 918 dsp::lr_to_ms(c->vDisplay_y, c->vDisplay_x, c->vDisplay_y, c->vDisplay_x, query_size); 919 920 // In-place decimation: 921 size_t j = 0; 922 923 for (size_t i = 1; i < query_size; ++i) 924 { 925 float dx = c->vDisplay_x[i] - c->vDisplay_x[j]; 926 float dy = c->vDisplay_y[i] - c->vDisplay_y[j]; 927 float s = dx*dx + dy*dy; 928 929 if (s < DECIM_PRECISION) // Skip point 930 { 931 c->vDisplay_s[j] = lsp_max(c->vDisplay_s[i], c->vDisplay_s[j]); // Keep the strobe signal 932 continue; 933 } 934 935 // Add point to decimated array 936 ++j; 937 c->vDisplay_x[j] = c->vDisplay_x[i]; 938 c->vDisplay_y[j] = c->vDisplay_y[i]; 939 } 940 941 // Detect occasional jumps 942 size_t to_submit = j + 1; // Total number of decimated samples. 943 944 // Apply scaling and offset: 945 dsp::mul_k2(c->vDisplay_y, c->fVerStreamScale, to_submit); 946 dsp::add_k2(c->vDisplay_y, c->fVerStreamOffset, to_submit); 947 948 // x is to be scaled and offset only in XY mode 949 if ((c->enMode == CH_MODE_XY) || (c->enMode == CH_MODE_GONIOMETER)) 950 { 951 dsp::mul_k2(c->vDisplay_x, c->fHorStreamScale, to_submit); 952 dsp::add_k2(c->vDisplay_x, c->fHorStreamOffset, to_submit); 953 } 954 955 // #ifdef LSP_TRACE 956 // for (size_t i=1; i < to_submit; ++i) 957 // { 958 // float dx = c->vDisplay_x[i] - c->vDisplay_x[i-1]; 959 // float dy = c->vDisplay_y[i] - c->vDisplay_y[i-1]; 960 // float s = dx*dx + dy*dy; 961 // if ((s >= 0.125f) && (c->vDisplay_s[i] <= 0.5f)) 962 // { 963 // lsp_trace("debug"); 964 // } 965 // } 966 // #endif 967 968 // Submit data for plotting (emit the figure data with fixed-size frames): 969 for (size_t i = 0; i < to_submit; ) // nSweepSize can be as big as BUF_LIM_SIZE !!! 970 { 971 size_t count = stream->add_frame(to_submit - i); // Add a frame 972 stream->write_frame(0, &c->vDisplay_x[i], 0, count); // X'es 973 stream->write_frame(1, &c->vDisplay_y[i], 0, count); // Y's 974 stream->write_frame(2, &c->vDisplay_s[i], 0, count); // Strobe signal 975 stream->commit_frame(); // Commit the frame 976 977 // Move the index in the source buffer 978 i += count; 979 } 980 981 // Is there data to submit to inline display? 982 if (to_submit > 0) 983 { 984 // Compute the start point to submit data 985 j = 0; 986 987 // In-place decimation: 988 for (size_t i = j+1; i < to_submit; ++i) 989 { 990 float dx = c->vDisplay_x[i] - c->vDisplay_x[j]; 991 float dy = c->vDisplay_y[i] - c->vDisplay_y[j]; 992 float s = dx*dx + dy*dy; 993 994 if (s < IDISPLAY_DECIM) // Skip point 995 continue; 996 997 // Add point to decimated array 998 ++j; 999 c->vDisplay_x[j] = c->vDisplay_x[i]; 1000 c->vDisplay_y[j] = c->vDisplay_y[i]; 1001 } 1002 1003 // Copy display data to inline display buffer. 1004 c->nIDisplay = j + 1; 1005 dsp::copy(c->vIDisplay_x, c->vDisplay_x, c->nIDisplay); 1006 dsp::copy(c->vIDisplay_y, c->vDisplay_y, c->nIDisplay); 1007 } 1008 1009 return true; 1010 } 1011 update_settings()1012 void oscilloscope_base::update_settings() 1013 { 1014 float xy_rectime = pXYRecordTime->getValue(); 1015 bool g_freeze = pFreeze->getValue() >= 0.5f; 1016 bool has_solo = false; 1017 1018 for (size_t ch = 0; ch < nChannels; ++ch) 1019 { 1020 channel_t *c = &vChannels[ch]; 1021 bool solo = (c->pSoloSwitch != NULL) ? c->pSoloSwitch->getValue() >= 0.5f : false; 1022 if (solo) 1023 has_solo = true; 1024 } 1025 1026 for (size_t ch = 0; ch < nChannels; ++ch) 1027 { 1028 channel_t *c = &vChannels[ch]; 1029 1030 // Global controls only actually exist for mult-channel plugins. Do not use for 1X. 1031 if (nChannels > 1) 1032 c->bUseGlobal = c->pGlobalSwitch->getValue() >= 0.5f; 1033 1034 bool solo = (c->pSoloSwitch != NULL) ? c->pSoloSwitch->getValue() >= 0.5f : false; 1035 bool mute = (c->pMuteSwitch != NULL) ? c->pMuteSwitch->getValue() >= 0.5f : false; 1036 c->bVisible = (has_solo) ? solo : !mute; 1037 1038 c->bFreeze = g_freeze; 1039 if ((!c->bFreeze) && (nChannels > 1)) 1040 c->bFreeze = c->pFreezeSwitch->getValue() >= 0.5f; 1041 1042 if (xy_rectime != c->sStateStage.fPV_pXYRecordTime) 1043 { 1044 c->sStateStage.fPV_pXYRecordTime = xy_rectime; 1045 c->nUpdate |= UPD_XY_RECORD_TIME; 1046 } 1047 1048 size_t scpmode = (c->bUseGlobal) ? pScpMode->getValue() : c->pScpMode->getValue(); 1049 if (scpmode != c->sStateStage.nPV_pScpMode) 1050 { 1051 c->sStateStage.nPV_pScpMode = scpmode; 1052 c->nUpdate |= UPD_SCPMODE; 1053 } 1054 1055 size_t coupling_x = (c->bUseGlobal) ? pCoupling_x->getValue() : c->pCoupling_x->getValue(); 1056 if (coupling_x != c->sStateStage.nPV_pCoupling_x) 1057 { 1058 c->sStateStage.nPV_pCoupling_x = coupling_x; 1059 c->nUpdate |= UPD_ACBLOCK_X; 1060 } 1061 1062 size_t coupling_y = (c->bUseGlobal) ? pCoupling_y->getValue() : c->pCoupling_y->getValue(); 1063 if (coupling_y != c->sStateStage.nPV_pCoupling_y) 1064 { 1065 c->sStateStage.nPV_pCoupling_y = coupling_y; 1066 c->nUpdate |= UPD_ACBLOCK_Y; 1067 } 1068 1069 size_t coupling_ext = (c->bUseGlobal) ? pCoupling_ext->getValue() : c->pCoupling_ext->getValue(); 1070 if (coupling_ext != c->sStateStage.nPV_pCoupling_ext) 1071 { 1072 c->sStateStage.nPV_pCoupling_ext = coupling_ext; 1073 c->nUpdate |= UPD_ACBLOCK_EXT; 1074 } 1075 1076 size_t overmode = (c->bUseGlobal) ? pOvsMode->getValue() : c->pOvsMode->getValue(); 1077 if (overmode != c->sStateStage.nPV_pOvsMode) 1078 { 1079 c->sStateStage.nPV_pOvsMode = overmode; 1080 c->nUpdate |= UPD_OVERSAMPLER_X | UPD_OVERSAMPLER_Y | UPD_OVERSAMPLER_EXT | 1081 UPD_PRETRG_DELAY | UPD_SWEEP_GENERATOR | UPD_TRIGGER_HOLD | 1082 UPD_XY_RECORD_TIME | UPD_SWEEP_GENERATOR; 1083 } 1084 1085 size_t trginput = (c->bUseGlobal) ? pTrgInput->getValue() : c->pTrgInput->getValue(); 1086 if (trginput != c->sStateStage.nPV_pTrgInput) 1087 { 1088 c->sStateStage.nPV_pTrgInput = trginput; 1089 c->nUpdate |= UPD_TRIGGER_INPUT; 1090 } 1091 1092 float verDiv = (c->bUseGlobal) ? pVerDiv->getValue() : c->pVerDiv->getValue(); 1093 float verPos = (c->bUseGlobal) ? pVerPos->getValue() : c->pVerPos->getValue(); 1094 if ((verDiv != c->sStateStage.fPV_pVerDiv) || (verPos != c->sStateStage.fPV_pVerPos)) 1095 { 1096 c->sStateStage.fPV_pVerDiv = verDiv; 1097 c->sStateStage.fPV_pVerPos = verPos; 1098 c->nUpdate |= UPD_VER_SCALES | UPD_TRIGGER; 1099 } 1100 1101 float trgHys = (c->bUseGlobal) ? pTrgHys->getValue() : c->pTrgHys->getValue(); 1102 if (trgHys != c->sStateStage.fPV_pTrgHys) 1103 { 1104 c->sStateStage.fPV_pTrgHys = trgHys; 1105 c->nUpdate |= UPD_TRIGGER; 1106 } 1107 1108 float trgLevel = (c->bUseGlobal) ? pTrgLev->getValue() : c->pTrgLev->getValue(); 1109 if (trgLevel != c->sStateStage.fPV_pTrgLevel) 1110 { 1111 c->sStateStage.fPV_pTrgLevel = trgLevel; 1112 c->nUpdate |= UPD_TRIGGER; 1113 } 1114 1115 size_t trgmode = (c->bUseGlobal) ? pTrgMode->getValue() : c->pTrgMode->getValue(); 1116 if (trgmode != c->sStateStage.nPV_pTrgMode) 1117 { 1118 c->sStateStage.nPV_pTrgMode = trgmode; 1119 c->nUpdate |= UPD_TRIGGER; 1120 } 1121 1122 float trghold = (c->bUseGlobal) ? pTrgHold->getValue() : c->pTrgHold->getValue(); 1123 if (trghold != c->sStateStage.fPV_pTrgHold) 1124 { 1125 c->sStateStage.fPV_pTrgHold = trghold; 1126 c->nUpdate |= UPD_TRIGGER_HOLD; 1127 } 1128 1129 size_t trgtype = (c->bUseGlobal) ? pTrgType->getValue() : c->pTrgType->getValue(); 1130 if (trgtype != c->sStateStage.nPV_pTrgType) 1131 { 1132 c->sStateStage.nPV_pTrgType = trgtype; 1133 c->nUpdate |= UPD_TRIGGER; 1134 } 1135 1136 float trg_reset = (c->bUseGlobal) ? pTrgReset->getValue() : c->pTrgReset->getValue(); 1137 if (trg_reset >= 0.5f) 1138 c->nUpdate |= UPD_TRGGER_RESET; 1139 1140 float timeDiv = (c->bUseGlobal) ? pTimeDiv->getValue() : c->pTimeDiv->getValue(); 1141 if (timeDiv != c->sStateStage.fPV_pTimeDiv) 1142 { 1143 c->sStateStage.fPV_pTimeDiv = timeDiv; 1144 c->nUpdate |= UPD_PRETRG_DELAY | UPD_SWEEP_GENERATOR | UPD_TRIGGER_HOLD; 1145 } 1146 1147 float horDiv = (c->bUseGlobal) ? pHorDiv->getValue() : c->pHorDiv->getValue(); 1148 if (timeDiv != c->sStateStage.fPV_pHorDiv) 1149 { 1150 c->sStateStage.fPV_pHorDiv = horDiv; 1151 c->nUpdate |= UPD_HOR_SCALES; 1152 } 1153 1154 float horPos = (c->bUseGlobal) ? pHorPos->getValue() : c->pHorPos->getValue(); 1155 if (horPos != c->sStateStage.fPV_pHorPos) 1156 { 1157 c->sStateStage.fPV_pHorPos = horPos; 1158 c->nUpdate |= UPD_HOR_SCALES | UPD_PRETRG_DELAY | UPD_SWEEP_GENERATOR; 1159 } 1160 1161 size_t sweeptype = (c->bUseGlobal) ? pSweepType->getValue() : c->pSweepType->getValue(); 1162 if (sweeptype != c->sStateStage.nPV_pSweepType) 1163 { 1164 c->sStateStage.nPV_pSweepType = sweeptype; 1165 c->nUpdate |= UPD_SWEEP_GENERATOR; 1166 } 1167 } 1168 } 1169 update_sample_rate(long sr)1170 void oscilloscope_base::update_sample_rate(long sr) 1171 { 1172 reconfigure_dc_block_filters(); 1173 1174 for (size_t ch = 0; ch < nChannels; ++ch) 1175 { 1176 channel_t *c = &vChannels[ch]; 1177 1178 c->sOversampler_x.set_sample_rate(sr); 1179 c->sOversampler_x.update_settings(); 1180 1181 c->sOversampler_y.set_sample_rate(sr); 1182 c->sOversampler_y.update_settings(); 1183 1184 c->sOversampler_ext.set_sample_rate(sr); 1185 c->sOversampler_ext.update_settings(); 1186 1187 c->nOverSampleRate = c->nOversampling * sr; 1188 1189 c->sSweepGenerator.set_sample_rate(sr); 1190 c->sSweepGenerator.update_settings(); 1191 } 1192 } 1193 process(size_t samples)1194 void oscilloscope_base::process(size_t samples) 1195 { 1196 // Prepare channels 1197 for (size_t ch = 0; ch < nChannels; ++ch) 1198 { 1199 channel_t *c = &vChannels[ch]; 1200 1201 c->vIn_x = c->pIn_x->getBuffer<float>(); 1202 c->vIn_y = c->pIn_y->getBuffer<float>(); 1203 c->vIn_ext = c->pIn_ext->getBuffer<float>(); 1204 1205 c->vOut_x = c->pOut_x->getBuffer<float>(); 1206 c->vOut_y = c->pOut_y->getBuffer<float>(); 1207 1208 if ((c->vIn_x == NULL) || (c->vIn_y == NULL)) 1209 return; 1210 1211 if ((c->vIn_ext == NULL)) 1212 return; 1213 1214 c->nSamplesCounter = samples; 1215 } 1216 1217 // Bypass signal 1218 for (size_t ch = 0; ch < nChannels; ++ch) 1219 { 1220 channel_t *c = &vChannels[ch]; 1221 1222 if (c->vOut_x != NULL) 1223 dsp::copy(c->vOut_x, c->vIn_x, samples); 1224 if (c->vOut_y != NULL) 1225 dsp::copy(c->vOut_y, c->vIn_y, samples); 1226 } 1227 1228 bool query_draw = false; 1229 1230 // Process each channel 1231 for (size_t ch = 0; ch < nChannels; ++ch) 1232 { 1233 channel_t *c = &vChannels[ch]; 1234 1235 commit_staged_state_change(c); 1236 1237 while (c->nSamplesCounter > 0) 1238 { 1239 size_t requested = c->nOversampling * c->nSamplesCounter; 1240 size_t availble = BUF_LIM_SIZE; 1241 size_t to_do_upsample = (requested < availble) ? requested : availble; 1242 size_t to_do = to_do_upsample / c->nOversampling; 1243 1244 switch (c->enMode) 1245 { 1246 case CH_MODE_XY: 1247 case CH_MODE_GONIOMETER: 1248 { 1249 if (c->enCoupling_x == CH_COUPLING_AC) 1250 { 1251 c->sDCBlockBank_x.process(c->vTemp, c->vIn_x, to_do); 1252 c->sOversampler_x.upsample(c->vData_x, c->vTemp, to_do); 1253 } 1254 else 1255 c->sOversampler_x.upsample(c->vData_x, c->vIn_x, to_do); 1256 1257 if (c->enCoupling_y == CH_COUPLING_AC) 1258 { 1259 c->sDCBlockBank_y.process(c->vTemp, c->vIn_y, to_do); 1260 c->sOversampler_y.upsample(c->vData_y, c->vTemp, to_do); 1261 } 1262 else 1263 c->sOversampler_y.upsample(c->vData_y, c->vIn_y, to_do); 1264 1265 for (size_t n = 0; n < to_do_upsample; ) 1266 { 1267 ssize_t count = lsp_min(ssize_t(c->nXYRecordSize - c->nDisplayHead), ssize_t(to_do_upsample - n)); 1268 if (count <= 0) 1269 { 1270 // Plot time! 1271 if (graph_stream(c)) 1272 query_draw = true; 1273 continue; 1274 } 1275 1276 // Move data to intermediate buffers 1277 dsp::copy(&c->vDisplay_x[c->nDisplayHead], &c->vData_x[n], count); 1278 dsp::copy(&c->vDisplay_y[c->nDisplayHead], &c->vData_y[n], count); 1279 dsp::fill_zero(&c->vDisplay_s[c->nDisplayHead], count); 1280 if (c->nDisplayHead == 0) 1281 c->vDisplay_s[0] = 1.0f; 1282 1283 // Update pointers 1284 c->nDisplayHead += count; 1285 n += count; 1286 } 1287 1288 } 1289 break; 1290 1291 case CH_MODE_TRIGGERED: 1292 { 1293 if (c->enCoupling_y == CH_COUPLING_AC) 1294 { 1295 c->sDCBlockBank_y.process(c->vTemp, c->vIn_y, to_do); 1296 c->sOversampler_y.upsample(c->vData_y, c->vTemp, to_do); 1297 } 1298 else 1299 c->sOversampler_y.upsample(c->vData_y, c->vIn_y, to_do); 1300 1301 c->sPreTrgDelay.process(c->vData_y_delay, c->vData_y, to_do_upsample); 1302 1303 if (c->enCoupling_ext == CH_COUPLING_AC) 1304 { 1305 c->sDCBlockBank_ext.process(c->vTemp, c->vIn_ext, to_do); 1306 c->sOversampler_ext.upsample(c->vData_ext, c->vTemp, to_do); 1307 } 1308 else 1309 c->sOversampler_ext.upsample(c->vData_ext, c->vIn_ext, to_do); 1310 1311 c->nDataHead = 0; 1312 1313 const float *trg_input = select_trigger_input(c->vData_ext, c->vData_y, c->enTrgInput); 1314 1315 for (size_t n = 0; n < to_do_upsample; ++n) 1316 { 1317 c->sTrigger.single_sample_processor(trg_input[n]); 1318 1319 switch (c->enState) 1320 { 1321 case CH_STATE_LISTENING: 1322 { 1323 bool sweep = c->sTrigger.get_trigger_state() == TRG_STATE_FIRED; 1324 if ((!sweep) && (c->bAutoSweep)) 1325 sweep = ((c->nAutoSweepCounter++) >= c->nAutoSweepLimit); 1326 1327 // No sweep triggered? 1328 if (!sweep) 1329 break; 1330 1331 c->sSweepGenerator.reset_phase_accumulator(); 1332 c->nDataHead = n; 1333 c->enState = CH_STATE_SWEEPING; 1334 c->nAutoSweepCounter = 0; 1335 c->nDisplayHead = 0; 1336 1337 do_sweep_step(c, 1.0f); 1338 1339 break; 1340 } 1341 1342 case CH_STATE_SWEEPING: 1343 do_sweep_step(c, 0.0f); 1344 1345 if (c->nDisplayHead >= c->nSweepSize) 1346 { 1347 // Plot time! 1348 if (graph_stream(c)) 1349 query_draw = true; 1350 c->enState = CH_STATE_LISTENING; 1351 } 1352 break; 1353 } 1354 } 1355 } 1356 break; 1357 } 1358 1359 c->vIn_x += to_do; 1360 c->vIn_y += to_do; 1361 c->vIn_ext += to_do; 1362 c->vOut_x += to_do; 1363 c->vOut_y += to_do; 1364 c->nSamplesCounter -= to_do; 1365 } 1366 } 1367 1368 if ((pWrapper != NULL) && (query_draw)) 1369 pWrapper->query_display_draw(); 1370 } 1371 dump(IStateDumper * v) const1372 void oscilloscope_base::dump(IStateDumper *v) const 1373 { 1374 plugin_t::dump(v); 1375 1376 v->begin_object("sDCBlockParams", &sDCBlockParams, sizeof(sDCBlockParams)); 1377 { 1378 v->write("fAlpha", sDCBlockParams.fAlpha); 1379 v->write("fGain", sDCBlockParams.fGain); 1380 } 1381 v->end_object(); 1382 1383 v->write("nChannels", nChannels); 1384 1385 v->begin_array("vChannels", vChannels, nChannels); 1386 for (size_t i = 0; i < nChannels; ++i) 1387 { 1388 const channel_t *c = &vChannels[i]; 1389 1390 v->begin_object(c, sizeof(channel_t)); 1391 { 1392 v->write("enMode", &c->enMode); 1393 v->write("enSweepType", &c->enSweepType); 1394 v->write("enTrgInput", &c->enTrgInput); 1395 v->write("enCoupling_x", &c->enCoupling_x); 1396 v->write("enCoupling_y", &c->enCoupling_y); 1397 v->write("enCoupling_ext", &c->enCoupling_ext); 1398 1399 v->write_object("sDCBlockBank_x", &c->sDCBlockBank_x); 1400 v->write_object("sDCBlockBank_y", &c->sDCBlockBank_y); 1401 v->write_object("sDCBlockBank_ext", &c->sDCBlockBank_ext); 1402 1403 v->write("enOverMode", &c->enOverMode); 1404 v->write("nOversampling", &c->nOversampling); 1405 v->write("nOverSampleRate", &c->nOverSampleRate); 1406 1407 v->write_object("sOversampler_x", &c->sOversampler_x); 1408 v->write_object("sOversampler_y", &c->sOversampler_y); 1409 v->write_object("sOversampler_ext", &c->sOversampler_ext); 1410 1411 v->write_object("sPreTrgDelay", &c->sPreTrgDelay); 1412 1413 v->write_object("sTrigger", &c->sTrigger); 1414 1415 v->write_object("sSweepGenerator", &c->sSweepGenerator); 1416 1417 v->write("vTemp", &c->vTemp); 1418 v->write("vData_x", &c->vData_x); 1419 v->write("vData_y", &c->vData_y); 1420 v->write("vData_ext", &c->vData_ext); 1421 v->write("vData_y_delay", &c->vData_y_delay); 1422 v->write("vDisplay_x", &c->vDisplay_x); 1423 v->write("vDisplay_y", &c->vDisplay_y); 1424 v->write("vDisplay_s", &c->vDisplay_s); 1425 1426 v->write("vIDisplay_x", &c->vIDisplay_x); 1427 v->write("vIDisplay_y", &c->vIDisplay_y); 1428 v->write("nIDisplay", &c->nIDisplay); 1429 1430 v->write("nDataHead", &c->nDataHead); 1431 v->write("nDisplayHead", &c->nDisplayHead); 1432 v->write("nSamplesCounter", &c->nSamplesCounter); 1433 v->write("bClearStream", &c->bClearStream); 1434 1435 v->write("nPreTrigger", &c->nPreTrigger); 1436 v->write("nSweepSize", &c->nSweepSize); 1437 1438 v->write("fVerStreamScale", &c->fVerStreamScale); 1439 v->write("fVerStreamOffset", &c->fVerStreamOffset); 1440 1441 v->write("nXYRecordSize", &c->nXYRecordSize); 1442 v->write("fHorStreamScale", &c->fHorStreamScale); 1443 v->write("fHorStreamOffset", &c->fHorStreamOffset); 1444 1445 v->write("bAutoSweep", &c->bAutoSweep); 1446 v->write("nAutoSweepLimit", &c->nAutoSweepLimit); 1447 v->write("nAutoSweepCounter", &c->nAutoSweepCounter); 1448 1449 v->write("enState", &c->enState); 1450 1451 v->write("nUpdate", &c->nUpdate); 1452 1453 v->begin_object("sStateStage", &c->sStateStage, sizeof(c->sStateStage)); 1454 { 1455 v->write("nPV_pScpMode", &c->sStateStage.nPV_pScpMode); 1456 1457 v->write("nPV_pCoupling_x", &c->sStateStage.nPV_pCoupling_x); 1458 v->write("nPV_pCoupling_y", &c->sStateStage.nPV_pCoupling_y); 1459 v->write("nPV_pCoupling_ext", &c->sStateStage.nPV_pCoupling_ext); 1460 v->write("nPV_pOvsMode", &c->sStateStage.nPV_pOvsMode); 1461 1462 v->write("nPV_pTrgInput", &c->sStateStage.nPV_pTrgInput); 1463 v->write("fPV_pVerDiv", &c->sStateStage.fPV_pVerDiv); 1464 v->write("fPV_pVerPos", &c->sStateStage.fPV_pVerPos); 1465 v->write("fPV_pTrgLevel", &c->sStateStage.fPV_pTrgLevel); 1466 v->write("fPV_pTrgHys", &c->sStateStage.fPV_pTrgHys); 1467 v->write("nPV_pTrgMode", &c->sStateStage.nPV_pTrgMode); 1468 v->write("fPV_pTrgHold", &c->sStateStage.fPV_pTrgHold); 1469 v->write("nPV_pTrgType", &c->sStateStage.nPV_pTrgType); 1470 1471 v->write("fPV_pTimeDiv", &c->sStateStage.fPV_pTimeDiv); 1472 v->write("fPV_pHorPos", &c->sStateStage.fPV_pHorPos); 1473 1474 v->write("nPV_pSweepType", &c->sStateStage.nPV_pSweepType); 1475 1476 v->write("fPV_pXYRecordTime", &c->sStateStage.fPV_pXYRecordTime); 1477 } 1478 v->end_object(); 1479 1480 v->write("bUseGlobal", &c->bUseGlobal); 1481 v->write("bFreeze", &c->bFreeze); 1482 1483 v->write("vIn_x", &c->vIn_x); 1484 v->write("vIn_y", &c->vIn_y); 1485 v->write("vIn_ext", &c->vIn_ext); 1486 1487 v->write("vOut_x", &c->vOut_x); 1488 v->write("vOut_y", &c->vOut_y); 1489 1490 v->write("pIn_x", &c->pIn_x); 1491 v->write("pIn_y", &c->pIn_y); 1492 v->write("pIn_ext", &c->pIn_ext); 1493 1494 v->write("pOut_x", &c->pOut_x); 1495 v->write("pOut_y", &c->pOut_y); 1496 1497 v->write("pOvsMode", &c->pOvsMode); 1498 v->write("pScpMode", &c->pScpMode); 1499 v->write("pCoupling_x", &c->pCoupling_x); 1500 v->write("pCoupling_y", &c->pCoupling_y); 1501 v->write("pCoupling_ext", &c->pCoupling_ext); 1502 1503 v->write("pSweepType", &c->pSweepType); 1504 v->write("pTimeDiv", &c->pTimeDiv); 1505 v->write("pHorDiv", &c->pHorDiv); 1506 v->write("pHorPos", &c->pHorPos); 1507 1508 v->write("pVerDiv", &c->pVerDiv); 1509 v->write("pVerPos", &c->pVerPos); 1510 1511 v->write("pTrgHys", &c->pTrgHys); 1512 v->write("pTrgLev", &c->pTrgLev); 1513 v->write("pTrgHold", &c->pTrgHold); 1514 v->write("pTrgMode", &c->pTrgMode); 1515 v->write("pTrgType", &c->pTrgType); 1516 v->write("pTrgInput", &c->pTrgInput); 1517 v->write("pTrgReset", &c->pTrgReset); 1518 1519 v->write("pGlobalSwitch", &c->pGlobalSwitch); 1520 v->write("pFreezeSwitch", &c->pFreezeSwitch); 1521 v->write("pSoloSwitch", &c->pSoloSwitch); 1522 v->write("pMuteSwitch", &c->pMuteSwitch); 1523 1524 v->write("pStream", &c->pStream); 1525 } 1526 v->end_object(); 1527 } 1528 v->end_array(); 1529 1530 v->write("pData", pData); 1531 1532 v->write("pStrobeHistSize", pStrobeHistSize); 1533 v->write("pXYRecordTime", pXYRecordTime); 1534 v->write("pFreeze", pFreeze); 1535 1536 v->write("pChannelSelector", pChannelSelector); 1537 1538 v->write("pOvsMode", pOvsMode); 1539 v->write("pScpMode", pScpMode); 1540 v->write("pCoupling_x", pCoupling_x); 1541 v->write("pCoupling_y", pCoupling_y); 1542 v->write("pCoupling_ext", pCoupling_ext); 1543 1544 v->write("pSweepType", pSweepType); 1545 v->write("pTimeDiv", pTimeDiv); 1546 v->write("pHorDiv", pHorDiv); 1547 v->write("pHorPos", pHorPos); 1548 1549 v->write("pVerDiv", pVerDiv); 1550 v->write("pVerPos", pVerPos); 1551 1552 v->write("pTrgHys", pTrgHys); 1553 v->write("pTrgLev", pTrgLev); 1554 v->write("pTrgHold", pTrgHold); 1555 v->write("pTrgMode", pTrgMode); 1556 v->write("pTrgType", pTrgType); 1557 v->write("pTrgInput", pTrgInput); 1558 v->write("pTrgReset", pTrgReset); 1559 1560 v->write("pIDisplay", pIDisplay); 1561 } 1562 1563 static const uint32_t ch_colors[] = 1564 { 1565 // x1 1566 0x0a9bff, 1567 // x2 1568 0xff0e11, 1569 0x0a9bff, 1570 // x4 1571 0xff0e11, 1572 0x12ff16, 1573 0xff6c11, 1574 0x0a9bff 1575 }; 1576 inline_display(ICanvas * cv,size_t width,size_t height)1577 bool oscilloscope_base::inline_display(ICanvas *cv, size_t width, size_t height) 1578 { 1579 // Check proportions 1580 if (height > width) 1581 height = width; 1582 1583 // Init canvas 1584 if (!cv->init(width, height)) 1585 return false; 1586 width = cv->width(); 1587 height = cv->height(); 1588 float cx = width >> 1; 1589 float cy = height >> 1; 1590 1591 // Clear background 1592 cv->paint(); 1593 1594 // Draw axis 1595 cv->set_line_width(1.0); 1596 cv->set_color_rgb(CV_SILVER, 0.5f); 1597 cv->line(0, 0, width, height); 1598 cv->line(0, height, width, 0); 1599 1600 cv->set_color_rgb(CV_WHITE, 0.5f); 1601 cv->line(cx, 0, cx, height); 1602 cv->line(0, cy, width, cy); 1603 1604 // Check for solos: 1605 const uint32_t *cols = 1606 (nChannels < 2) ? &ch_colors[0] : 1607 (nChannels < 4) ? &ch_colors[1] : 1608 &ch_colors[3]; 1609 1610 float halfv = 0.5f * width; 1611 float halfh = 0.5f * height; 1612 1613 // Estimate the display length 1614 size_t di_length = 1; 1615 for (size_t ch = 0; ch < nChannels; ++ch) 1616 di_length = lsp_max(di_length, vChannels[ch].nIDisplay); 1617 1618 // Allocate buffer: t, f(t) 1619 pIDisplay = float_buffer_t::reuse(pIDisplay, 2, di_length); 1620 float_buffer_t *b = pIDisplay; 1621 if (b == NULL) 1622 return false; 1623 1624 bool aa = cv->set_anti_aliasing(true); 1625 1626 for (size_t ch = 0; ch < nChannels; ++ch) 1627 { 1628 channel_t *c = &vChannels[ch]; 1629 if (!c->bVisible) 1630 continue; 1631 1632 size_t dlen = lsp_min(c->nIDisplay, di_length); 1633 for (size_t i=0; i<dlen; ++i) 1634 { 1635 b->v[0][i] = halfv * (c->vIDisplay_x[i] + 1.0f); 1636 b->v[1][i] = halfh * (-c->vIDisplay_y[i] + 1.0f); 1637 } 1638 1639 // Set color and draw 1640 cv->set_color_rgb(cols[ch]); 1641 cv->set_line_width(2); 1642 cv->draw_lines(b->v[0], b->v[1], dlen); 1643 } 1644 1645 cv->set_anti_aliasing(aa); 1646 1647 return true; 1648 } 1649 oscilloscope_x1()1650 oscilloscope_x1::oscilloscope_x1(): oscilloscope_base(metadata, 1) 1651 { 1652 } 1653 ~oscilloscope_x1()1654 oscilloscope_x1::~oscilloscope_x1() 1655 { 1656 } 1657 oscilloscope_x2()1658 oscilloscope_x2::oscilloscope_x2(): oscilloscope_base(metadata, 2) 1659 { 1660 } 1661 ~oscilloscope_x2()1662 oscilloscope_x2::~oscilloscope_x2() 1663 { 1664 } 1665 oscilloscope_x4()1666 oscilloscope_x4::oscilloscope_x4(): oscilloscope_base(metadata, 4) 1667 { 1668 } 1669 ~oscilloscope_x4()1670 oscilloscope_x4::~oscilloscope_x4() 1671 { 1672 } 1673 } 1674