pxt-microbit 4.1.18 → 4.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/built/sim.js CHANGED
@@ -2361,699 +2361,62 @@ var pxsim;
2361
2361
  serial.setBaudRate = setBaudRate;
2362
2362
  })(serial = pxsim.serial || (pxsim.serial = {}));
2363
2363
  })(pxsim || (pxsim = {}));
2364
- /**
2365
- * Adapted from lancaster-university/codal-microbit-v2
2366
- * https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/SoundEmojiSynthesizer.cpp
2367
- */
2368
2364
  var pxsim;
2369
2365
  (function (pxsim) {
2370
2366
  var music;
2371
2367
  (function (music) {
2372
- // https://github.com/lancaster-university/codal-microbit-v2/blob/master/inc/SoundEmojiSynthesizer.h#L30
2373
- music.EMOJI_SYNTHESIZER_SAMPLE_RATE = 44100;
2374
- music.EMOJI_SYNTHESIZER_TONE_WIDTH_F = 1024;
2375
- music.EMOJI_SYNTHESIZER_TONE_WIDTH = 1024;
2376
- music.EMOJI_SYNTHESIZER_BUFFER_SIZE = 512;
2377
- music.EMOJI_SYNTHESIZER_TONE_EFFECT_PARAMETERS = 2;
2378
- music.EMOJI_SYNTHESIZER_TONE_EFFECTS = 3;
2379
- music.EMOJI_SYNTHESIZER_STATUS_ACTIVE = 0x1;
2380
- music.EMOJI_SYNTHESIZER_STATUS_OUTPUT_SILENCE_AS_EMPTY = 0x2;
2381
- music.EMOJI_SYNTHESIZER_STATUS_STOPPING = 0x4;
2382
- class SoundEmojiSynthesizer {
2383
- constructor(id, sampleRate = music.EMOJI_SYNTHESIZER_SAMPLE_RATE) {
2384
- this.samplesPerStep = [];
2385
- this.status = 0;
2386
- this.effectPointer = 0;
2387
- this.position = 0;
2388
- this.bufferSize = music.EMOJI_SYNTHESIZER_BUFFER_SIZE;
2389
- this.sampleRate = sampleRate;
2390
- this.samplesToWrite = 0;
2391
- this.samplesWritten = 0;
2392
- this.sampleRange = 1023;
2393
- this.orMask = 0;
2394
- this.effectPointer = -1;
2395
- this.volume = 1;
2396
- }
2397
- get effect() {
2398
- return this.effectBuffer[this.effectPointer];
2399
- }
2400
- play(sound) {
2401
- this.effectBuffer = sound;
2402
- this.effectPointer = -1;
2403
- this.nextSoundEffect();
2404
- }
2405
- nextSoundEffect() {
2406
- const hadEffect = this.effect != null;
2407
- if (this.status & music.EMOJI_SYNTHESIZER_STATUS_STOPPING) {
2408
- this.effectPointer = null;
2409
- this.effectBuffer = [];
2410
- }
2411
- // If a sequence of SoundEffects are being played, attempt to move on to the next.
2412
- // If not, select the first in the buffer.
2413
- if (this.effect)
2414
- this.effectPointer++;
2415
- else
2416
- this.effectPointer = 0;
2417
- // Validate that we have a valid sound effect. If not, record that we have nothing to play.
2418
- if (this.effectPointer >= this.effectBuffer.length) {
2419
- // if we have an effect with a negative duration, reset the buffer (unless there is an update pending)
2420
- this.effectPointer = 0;
2421
- if (this.effect.duration >= 0) {
2422
- this.effectPointer = -1;
2423
- this.effectBuffer = [];
2424
- this.samplesWritten = 0;
2425
- this.samplesToWrite = 0;
2426
- this.position = 0;
2427
- return hadEffect;
2428
- }
2429
- }
2430
- // We have a valid buffer. Set up our synthesizer to the requested parameters.
2431
- this.samplesToWrite = this.determineSampleCount(this.effect.duration);
2432
- this.frequency = this.effect.frequency;
2433
- this.volume = this.effect.volume;
2434
- this.samplesWritten = 0;
2435
- // validate and initialise per effect rendering state.
2436
- for (let i = 0; i < music.EMOJI_SYNTHESIZER_TONE_EFFECTS; i++) {
2437
- this.effect.effects[i].step = 0;
2438
- this.effect.effects[i].steps = Math.max(this.effect.effects[i].steps, 1);
2439
- this.samplesPerStep[i] = Math.floor(this.samplesToWrite / this.effect.effects[i].steps);
2440
- }
2441
- return false;
2442
- }
2443
- pull() {
2444
- let done = false;
2445
- let sample;
2446
- let bufferEnd;
2447
- while (!done) {
2448
- if (this.samplesWritten == this.samplesToWrite || this.status & music.EMOJI_SYNTHESIZER_STATUS_STOPPING) {
2449
- let renderComplete = this.nextSoundEffect();
2450
- // If we have just completed active playout of an effect, and there are no more effects scheduled,
2451
- // unblock any fibers that may be waiting to play a sound effect.
2452
- if (this.samplesToWrite == 0 || this.status & music.EMOJI_SYNTHESIZER_STATUS_STOPPING) {
2453
- done = true;
2454
- if (renderComplete || this.status & music.EMOJI_SYNTHESIZER_STATUS_STOPPING) {
2455
- this.status &= ~music.EMOJI_SYNTHESIZER_STATUS_STOPPING;
2456
- // Event(id, DEVICE_SOUND_EMOJI_SYNTHESIZER_EVT_DONE);
2457
- // lock.notify();
2458
- }
2459
- }
2460
- }
2461
- // If we have something to do, ensure our buffers are created.
2462
- // We defer creation to avoid unnecessary heap allocation when generating silence.
2463
- if (((this.samplesWritten < this.samplesToWrite) || !(this.status & music.EMOJI_SYNTHESIZER_STATUS_OUTPUT_SILENCE_AS_EMPTY)) && sample == null) {
2464
- this.buffer = new Array(this.bufferSize);
2465
- sample = 0;
2466
- bufferEnd = this.buffer.length;
2467
- }
2468
- // Generate some samples with the current this.effect parameters.
2469
- while (this.samplesWritten < this.samplesToWrite) {
2470
- let skip = ((music.EMOJI_SYNTHESIZER_TONE_WIDTH_F * this.frequency) / this.sampleRate);
2471
- let gain = (this.sampleRange * this.volume) / 1024;
2472
- let offset = 512 - (512 * gain);
2473
- let effectStepEnd = [];
2474
- for (let i = 0; i < music.EMOJI_SYNTHESIZER_TONE_EFFECTS; i++) {
2475
- effectStepEnd[i] = (this.samplesPerStep[i] * (this.effect.effects[i].step));
2476
- if (this.effect.effects[i].step == this.effect.effects[i].steps - 1)
2477
- effectStepEnd[i] = this.samplesToWrite;
2478
- }
2479
- let stepEndPosition = effectStepEnd[0];
2480
- for (let i = 1; i < music.EMOJI_SYNTHESIZER_TONE_EFFECTS; i++)
2481
- stepEndPosition = Math.min(stepEndPosition, effectStepEnd[i]);
2482
- // Write samples until the end of the next this.effect-step
2483
- while (this.samplesWritten < stepEndPosition) {
2484
- // Stop processing when we've filled the requested this.buffer
2485
- if (sample == bufferEnd) {
2486
- // downStream.pullRequest();
2487
- return this.buffer;
2488
- }
2489
- // Synthesize a sample
2490
- let s = this.effect.tone.tonePrint(this.effect.tone.parameter, this.position);
2491
- // Apply volume scaling and OR mask (if specified).
2492
- this.buffer[sample] = (((s * gain) + offset)); // | this.orMask;
2493
- // Move on our pointers.
2494
- sample++;
2495
- this.samplesWritten++;
2496
- this.position += skip;
2497
- // Keep our toneprint pointer in range
2498
- while (this.position > music.EMOJI_SYNTHESIZER_TONE_WIDTH_F)
2499
- this.position -= music.EMOJI_SYNTHESIZER_TONE_WIDTH_F;
2500
- }
2501
- // Invoke the this.effect function for any effects that are due.
2502
- for (let i = 0; i < music.EMOJI_SYNTHESIZER_TONE_EFFECTS; i++) {
2503
- if (this.samplesWritten == effectStepEnd[i]) {
2504
- if (this.effect.effects[i].step < this.effect.effects[i].steps) {
2505
- if (this.effect.effects[i].effect)
2506
- this.effect.effects[i].effect(this, this.effect.effects[i]);
2507
- this.effect.effects[i].step++;
2508
- }
2509
- }
2510
- }
2511
- }
2512
- }
2513
- // if we have no data to send, return an empty this.buffer (if requested)
2514
- if (sample == null) {
2515
- this.buffer = [];
2516
- }
2517
- else {
2518
- // Pad the output this.buffer with silence if necessary.
2519
- const silence = (this.sampleRange * 0.5); // | this.orMask;
2520
- while (sample < bufferEnd) {
2521
- this.buffer[sample] = silence;
2522
- sample++;
2523
- }
2524
- }
2525
- // Issue a Pull Request so that we are always receiver driven, and we're done.
2526
- // downStream.pullRequest();
2527
- return this.buffer;
2528
- }
2529
- determineSampleCount(playoutTime) {
2530
- if (playoutTime < 0)
2531
- playoutTime = -playoutTime;
2532
- const seconds = playoutTime / 1000;
2533
- return Math.floor(this.sampleRate * seconds);
2534
- }
2535
- totalDuration() {
2536
- let duration = 0;
2537
- for (const effect of this.effectBuffer)
2538
- duration += effect.duration;
2539
- return duration;
2540
- }
2541
- }
2542
- music.SoundEmojiSynthesizer = SoundEmojiSynthesizer;
2543
- })(music = pxsim.music || (pxsim.music = {}));
2544
- })(pxsim || (pxsim = {}));
2545
- /**
2546
- * Adapted from lancaster-university/codal-microbit-v2
2547
- * https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/SoundExpressions.cpp#L286
2548
- */
2549
- var pxsim;
2550
- (function (pxsim) {
2551
- var music;
2552
- (function (music) {
2553
- var builtin;
2554
- (function (builtin) {
2555
- const giggle = "giggle";
2556
- const giggleData = "010230988019008440044008881023001601003300240000000000000000000000000000,110232570087411440044008880352005901003300010000000000000000010000000000,310232729021105440288908880091006300000000240700020000000000003000000000,310232729010205440288908880091006300000000240700020000000000003000000000,310232729011405440288908880091006300000000240700020000000000003000000000";
2557
- const happy = "happy";
2558
- const happyData = "010231992066911440044008880262002800001800020500000000000000010000000000,002322129029508440240408880000000400022400110000000000000000007500000000,000002129029509440240408880145000400022400110000000000000000007500000000";
2559
- const hello = "hello";
2560
- const helloData = "310230673019702440118708881023012800000000240000000000000000000000000000,300001064001602440098108880000012800000100040000000000000000000000000000,310231064029302440098108881023012800000100040000000000000000000000000000";
2561
- const mysterious = "mysterious";
2562
- const mysteriousData = "400002390033100440240408880477000400022400110400000000000000008000000000,405512845385000440044008880000012803010500160000000000000000085000500015";
2563
- const sad = "sad";
2564
- const sadData = "310232226070801440162408881023012800000100240000000000000000000000000000,310231623093602440093908880000012800000100240000000000000000000000000000";
2565
- const slide = "slide";
2566
- const slideData = "105202325022302440240408881023012801020000110400000000000000010000000000,010232520091002440044008881023012801022400110400000000000000010000000000";
2567
- const soaring = "soaring";
2568
- const soaringData = "210234009530905440599908881023002202000400020250000000000000020000000000,402233727273014440044008880000003101024400030000000000000000000000000000";
2569
- const spring = "spring";
2570
- const springData = "306590037116312440058708880807003400000000240000000000000000050000000000,010230037116313440058708881023003100000000240000000000000000050000000000";
2571
- const twinkle = "twinkle";
2572
- const twinkleData = "010180007672209440075608880855012800000000240000000000000000000000000000";
2573
- const yawn = "yawn";
2574
- const yawnData = "200002281133202440150008881023012801024100240400030000000000010000000000,005312520091002440044008880636012801022400110300000000000000010000000000,008220784019008440044008880681001600005500240000000000000000005000000000,004790784019008440044008880298001600000000240000000000000000005000000000,003210784019008440044008880108001600003300080000000000000000005000000000";
2575
- function lookupBuiltIn(sound) {
2576
- if (sound == giggle)
2577
- return giggleData;
2578
- if (sound == happy)
2579
- return happyData;
2580
- if (sound == hello)
2581
- return helloData;
2582
- if (sound == mysterious)
2583
- return mysteriousData;
2584
- if (sound == sad)
2585
- return sadData;
2586
- if (sound == slide)
2587
- return slideData;
2588
- if (sound == soaring)
2589
- return soaringData;
2590
- if (sound == spring)
2591
- return springData;
2592
- if (sound == twinkle)
2593
- return twinkleData;
2594
- if (sound == yawn)
2595
- return yawnData;
2596
- return sound;
2597
- }
2598
- builtin.lookupBuiltIn = lookupBuiltIn;
2599
- })(builtin = music.builtin || (music.builtin = {}));
2600
- })(music = pxsim.music || (pxsim.music = {}));
2601
- })(pxsim || (pxsim = {}));
2602
- /**
2603
- * Adapted from lancaster-university/codal-core
2604
- * https://github.com/lancaster-university/codal-core/blob/master/source/streams/Synthesizer.cpp#L54
2605
- */
2606
- var pxsim;
2607
- (function (pxsim) {
2608
- var music;
2609
- (function (music) {
2610
- var Synthesizer;
2611
- (function (Synthesizer) {
2612
- const sineTone = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 15, 16, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 37, 38, 40, 41, 42, 43, 45, 46, 47, 49, 50, 51, 53, 54, 56, 57, 58, 60, 61, 63, 64, 66, 68, 69, 71, 72, 74, 76, 77, 79, 81, 82, 84, 86, 87, 89, 91, 93, 95, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 141, 143, 145, 147, 149, 152, 154, 156, 158, 161, 163, 165, 167, 170, 172, 175, 177, 179, 182, 184, 187, 189, 191, 194, 196, 199, 201, 204, 206, 209, 211, 214, 216, 219, 222, 224, 227, 229, 232, 235, 237, 240, 243, 245, 248, 251, 253, 256, 259, 262, 264, 267, 270, 273, 275, 278, 281, 284, 287, 289, 292, 295, 298, 301, 304, 307, 309, 312, 315, 318, 321, 324, 327, 330, 333, 336, 339, 342, 345, 348, 351, 354, 357, 360, 363, 366, 369, 372, 375, 378, 381, 384, 387, 390, 393, 396, 399, 402, 405, 408, 411, 414, 417, 420, 424, 427, 430, 433, 436, 439, 442, 445, 448, 452, 455, 458, 461, 464, 467, 470, 473, 477, 480, 483, 486, 489, 492, 495, 498, 502, 505, 508, 511, 514, 517, 520, 524, 527, 530, 533, 536, 539, 542, 545, 549, 552, 555, 558, 561, 564, 567, 570, 574, 577, 580, 583, 586, 589, 592, 595, 598, 602, 605, 608, 611, 614, 617, 620, 623, 626, 629, 632, 635, 638, 641, 644, 647, 650, 653, 656, 659, 662, 665, 668, 671, 674, 677, 680, 683, 686, 689, 692, 695, 698, 701, 704, 707, 710, 713, 715, 718, 721, 724, 727, 730, 733, 735, 738, 741, 744, 747, 749, 752, 755, 758, 760, 763, 766, 769, 771, 774, 777, 779, 782, 785, 787, 790, 793, 795, 798, 800, 803, 806, 808, 811, 813, 816, 818, 821, 823, 826, 828, 831, 833, 835, 838, 840, 843, 845, 847, 850, 852, 855, 857, 859, 861, 864, 866, 868, 870, 873, 875, 877, 879, 881, 884, 886, 888, 890, 892, 894, 896, 898, 900, 902, 904, 906, 908, 910, 912, 914, 916, 918, 920, 922, 924, 926, 927, 929, 931, 933, 935, 936, 938, 940, 941, 943, 945, 946, 948, 950, 951, 953, 954, 956, 958, 959, 961, 962, 964, 965, 966, 968, 969, 971, 972, 973, 975, 976, 977, 979, 980, 981, 982, 984, 985, 986, 987, 988, 989, 990, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1006, 1007, 1008, 1009, 1009, 1010, 1011, 1011, 1012, 1013, 1013, 1014, 1014, 1015, 1015, 1016, 1016, 1017, 1017, 1018, 1018, 1019, 1019, 1019, 1020, 1020, 1020, 1021, 1021, 1021, 1021, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1023, 1022];
2613
- const TONE_WIDTH = 1024;
2614
- function SineTone(arg, position) {
2615
- position |= 0;
2616
- let off = TONE_WIDTH - position;
2617
- if (off < TONE_WIDTH / 2)
2618
- position = off;
2619
- return sineTone[position];
2620
- }
2621
- Synthesizer.SineTone = SineTone;
2622
- function SawtoothTone(arg, position) {
2623
- return position;
2624
- }
2625
- Synthesizer.SawtoothTone = SawtoothTone;
2626
- function TriangleTone(arg, position) {
2627
- return position < 512 ? position * 2 : (1023 - position) * 2;
2628
- }
2629
- Synthesizer.TriangleTone = TriangleTone;
2630
- function NoiseTone(arg, position) {
2631
- // deterministic, semi-random noise
2632
- let mult = arg[0];
2633
- if (mult == 0)
2634
- mult = 7919;
2635
- return (position * mult) & 1023;
2636
- }
2637
- Synthesizer.NoiseTone = NoiseTone;
2638
- function SquareWaveTone(arg, position) {
2639
- return position < 512 ? 1023 : 0;
2640
- }
2641
- Synthesizer.SquareWaveTone = SquareWaveTone;
2642
- })(Synthesizer = music.Synthesizer || (music.Synthesizer = {}));
2643
- })(music = pxsim.music || (pxsim.music = {}));
2644
- })(pxsim || (pxsim = {}));
2645
- /**
2646
- * Adapted from lancaster-university/codal-microbit-v2
2647
- * https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/SoundSynthesizerEffects.cpp
2648
- */
2649
- var pxsim;
2650
- (function (pxsim) {
2651
- var music;
2652
- (function (music) {
2653
- var SoundSynthesizerEffects;
2654
- (function (SoundSynthesizerEffects) {
2655
- /*
2656
- * Definitions of standard progressions.
2657
- */
2658
- /**
2659
- * Root Frequency Interpolation Effect Functions
2660
- */
2661
- function noInterpolation(synth, context) {
2662
- }
2663
- SoundSynthesizerEffects.noInterpolation = noInterpolation;
2664
- // Linear interpolate function.
2665
- // parameter[0]: end frequency
2666
- function linearInterpolation(synth, context) {
2667
- let interval = (context.parameter[0] - synth.effect.frequency) / context.steps;
2668
- synth.frequency = synth.effect.frequency + interval * context.step;
2669
- }
2670
- SoundSynthesizerEffects.linearInterpolation = linearInterpolation;
2671
- // Linear interpolate function.
2672
- // parameter[0]: end frequency
2673
- function logarithmicInterpolation(synth, context) {
2674
- synth.frequency = synth.effect.frequency + (Math.log10(context.step) * (context.parameter[0] - synth.effect.frequency) / 1.95);
2675
- }
2676
- SoundSynthesizerEffects.logarithmicInterpolation = logarithmicInterpolation;
2677
- // Curve interpolate function
2678
- // parameter[0]: end frequency
2679
- function curveInterpolation(synth, context) {
2680
- synth.frequency = (Math.sin(context.step * 3.12159 / 180.0) * (context.parameter[0] - synth.effect.frequency) + synth.effect.frequency);
2681
- }
2682
- SoundSynthesizerEffects.curveInterpolation = curveInterpolation;
2683
- // Cosine interpolate function
2684
- // parameter[0]: end frequency
2685
- function slowVibratoInterpolation(synth, context) {
2686
- synth.frequency = Math.sin(context.step / 10) * context.parameter[0] + synth.effect.frequency;
2687
- }
2688
- SoundSynthesizerEffects.slowVibratoInterpolation = slowVibratoInterpolation;
2689
- //warble function
2690
- // parameter[0]: end frequency
2691
- function warbleInterpolation(synth, context) {
2692
- synth.frequency = (Math.sin(context.step) * (context.parameter[0] - synth.effect.frequency) + synth.effect.frequency);
2693
- }
2694
- SoundSynthesizerEffects.warbleInterpolation = warbleInterpolation;
2695
- // Vibrato function
2696
- // parameter[0]: end frequency
2697
- function vibratoInterpolation(synth, context) {
2698
- synth.frequency = synth.effect.frequency + Math.sin(context.step) * context.parameter[0];
2699
- }
2700
- SoundSynthesizerEffects.vibratoInterpolation = vibratoInterpolation;
2701
- // Exponential rising function
2702
- // parameter[0]: end frequency
2703
- function exponentialRisingInterpolation(synth, context) {
2704
- synth.frequency = synth.effect.frequency + Math.sin(0.01745329 * context.step) * context.parameter[0];
2705
- }
2706
- SoundSynthesizerEffects.exponentialRisingInterpolation = exponentialRisingInterpolation;
2707
- // Exponential falling function
2708
- function exponentialFallingInterpolation(synth, context) {
2709
- synth.frequency = synth.effect.frequency + Math.cos(0.01745329 * context.step) * context.parameter[0];
2710
- }
2711
- SoundSynthesizerEffects.exponentialFallingInterpolation = exponentialFallingInterpolation;
2712
- // Argeppio functions
2713
- function appregrioAscending(synth, context) {
2714
- synth.frequency = music.MusicalProgressions.calculateFrequencyFromProgression(synth.effect.frequency, context.parameter_p[0], context.step);
2715
- }
2716
- SoundSynthesizerEffects.appregrioAscending = appregrioAscending;
2717
- function appregrioDescending(synth, context) {
2718
- synth.frequency = music.MusicalProgressions.calculateFrequencyFromProgression(synth.effect.frequency, context.parameter_p[0], context.steps - context.step - 1);
2719
- }
2720
- SoundSynthesizerEffects.appregrioDescending = appregrioDescending;
2721
- /**
2722
- * Frequency Delta effects
2723
- */
2724
- // Frequency vibrato function
2725
- // parameter[0]: vibrato frequency multiplier
2726
- function frequencyVibratoEffect(synth, context) {
2727
- if (context.step == 0)
2728
- return;
2729
- if (context.step % 2 == 0)
2730
- synth.frequency /= context.parameter[0];
2731
- else
2732
- synth.frequency *= context.parameter[0];
2733
- }
2734
- SoundSynthesizerEffects.frequencyVibratoEffect = frequencyVibratoEffect;
2735
- // Volume vibrato function
2736
- // parameter[0]: vibrato volume multiplier
2737
- function volumeVibratoEffect(synth, context) {
2738
- if (context.step == 0)
2739
- return;
2740
- if (context.step % 2 == 0)
2741
- synth.volume /= context.parameter[0];
2742
- else
2743
- synth.volume *= context.parameter[0];
2744
- }
2745
- SoundSynthesizerEffects.volumeVibratoEffect = volumeVibratoEffect;
2746
- /**
2747
- * Volume Delta effects
2748
- */
2749
- /** Simple ADSR enveleope effect.
2750
- * parameter[0]: Centre volume
2751
- * parameter[1]: End volume
2752
- * effect.volume: start volume
2753
- */
2754
- function adsrVolumeEffect(synth, context) {
2755
- let halfSteps = context.steps * 0.5;
2756
- if (context.step <= halfSteps) {
2757
- let delta = (context.parameter[0] - synth.effect.volume) / halfSteps;
2758
- synth.volume = synth.effect.volume + context.step * delta;
2759
- }
2760
- else {
2761
- let delta = (context.parameter[1] - context.parameter[0]) / halfSteps;
2762
- synth.volume = context.parameter[0] + (context.step - halfSteps) * delta;
2763
- }
2764
- }
2765
- SoundSynthesizerEffects.adsrVolumeEffect = adsrVolumeEffect;
2766
- /**
2767
- * Simple volume ramp effect
2768
- * parameter[0]: End volume
2769
- * effect.volume: start volume
2770
- */
2771
- function volumeRampEffect(synth, context) {
2772
- let delta = (context.parameter[0] - synth.effect.volume) / context.steps;
2773
- synth.volume = synth.effect.volume + context.step * delta;
2774
- }
2775
- SoundSynthesizerEffects.volumeRampEffect = volumeRampEffect;
2776
- })(SoundSynthesizerEffects = music.SoundSynthesizerEffects || (music.SoundSynthesizerEffects = {}));
2777
- })(music = pxsim.music || (pxsim.music = {}));
2778
- })(pxsim || (pxsim = {}));
2779
- var pxsim;
2780
- (function (pxsim) {
2781
- var music;
2782
- (function (music) {
2783
- let synth;
2784
- let soundPromise;
2785
2368
  //%
2786
2369
  function __playSoundExpression(notes, waitTillDone) {
2787
- const cb = pxsim.getResume();
2788
- const b = pxsim.board();
2789
- // v2 only...
2790
- b.ensureHardwareVersion(2);
2791
- if (!synth)
2792
- synth = new music.SoundEmojiSynthesizer(0);
2793
- if (!soundPromise)
2794
- soundPromise = Promise.resolve();
2795
- soundPromise = soundPromise.then(() => {
2796
- notes = music.builtin.lookupBuiltIn(notes);
2797
- const soundEffects = parseSoundEffects(notes);
2798
- synth.play(soundEffects);
2799
- let cancelled = false;
2800
- return Promise.race([
2801
- delayAsync(synth.totalDuration())
2802
- .then(() => {
2803
- // If safari didn't allow the sound to play for some reason,
2804
- // it will get delayed until the user does something that
2805
- // unmutes it. make sure we cancel it so that it doesn't
2806
- // play long after it was supposed to
2807
- cancelled = true;
2808
- }),
2809
- pxsim.AudioContextManager.playPCMBufferStreamAsync(() => {
2810
- if (!synth.effect)
2811
- return undefined;
2812
- const buff = synth.pull();
2813
- const arr = new Float32Array(buff.length);
2814
- for (let i = 0; i < buff.length; i++) {
2815
- // Buffer is (0, 1023) we need to map it to (-1, 1)
2816
- arr[i] = ((buff[i] - 512) / 512);
2817
- }
2818
- return arr;
2819
- }, synth.sampleRate, 0.03, () => cancelled)
2820
- ]);
2821
- });
2822
- if (!waitTillDone)
2823
- cb();
2824
- else
2825
- soundPromise = soundPromise.then(cb);
2370
+ notes = lookupBuiltIn(notes);
2371
+ pxsim.codal.music.__playSoundExpression(notes, waitTillDone);
2826
2372
  }
2827
2373
  music.__playSoundExpression = __playSoundExpression;
2828
2374
  function __stopSoundExpressions() {
2829
2375
  pxsim.AudioContextManager.stopAll();
2830
2376
  }
2831
2377
  music.__stopSoundExpressions = __stopSoundExpressions;
2832
- /**
2833
- * Adapted from lancaster-university/codal-microbit-v2
2834
- * https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/SoundExpressions.cpp
2835
- */
2836
- function parseSoundEffects(notes) {
2837
- // https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/SoundExpressions.cpp#L57
2838
- // 72 characters of sound data comma separated
2839
- const charsPerEffect = 72;
2840
- const effectCount = Math.floor((notes.length + 1) / (charsPerEffect + 1));
2841
- const expectedLength = effectCount * (charsPerEffect + 1) - 1;
2842
- if (notes.length != expectedLength) {
2843
- return [];
2844
- }
2845
- const soundEffects = [];
2846
- for (let i = 0; i < effectCount; ++i) {
2847
- const start = i * charsPerEffect + i;
2848
- if (start > 0 && notes[start - 1] != ',') {
2849
- return [];
2850
- }
2851
- const effect = blankSoundEffect();
2852
- if (!parseSoundExpression(notes.substr(start), effect)) {
2853
- return [];
2854
- }
2855
- soundEffects.push(effect);
2856
- }
2857
- return soundEffects;
2858
- }
2859
- function parseSoundExpression(soundChars, fx) {
2860
- // https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/SoundExpressions.cpp#L115
2861
- // Encoded as a sequence of zero padded decimal strings.
2862
- // This encoding is worth reconsidering if we can!
2863
- // The ADSR effect (and perhaps others in future) has two parameters which cannot be expressed.
2864
- // 72 chars total
2865
- // [0] 0-4 wave
2866
- let wave = parseInt(soundChars.substr(0, 1));
2867
- // [1] 0000-1023 volume
2868
- let effectVolume = parseInt(soundChars.substr(1, 4));
2869
- // [5] 0000-9999 frequency
2870
- let frequency = parseInt(soundChars.substr(5, 4));
2871
- // [9] 0000-9999 duration
2872
- let duration = parseInt(soundChars.substr(9, 4));
2873
- // [13] 00 shape (specific known values)
2874
- let shape = parseInt(soundChars.substr(13, 2));
2875
- // [15] XXX unused/bug. This was startFrequency but we use frequency above.
2876
- // [18] 0000-9999 end frequency
2877
- let endFrequency = parseInt(soundChars.substr(18, 4));
2878
- // [22] XXXX unused. This was start volume but we use volume above.
2879
- // [26] 0000-1023 end volume
2880
- let endVolume = parseInt(soundChars.substr(26, 4));
2881
- // [30] 0000-9999 steps
2882
- let steps = parseInt(soundChars.substr(30, 4));
2883
- // [34] 00-03 fx choice
2884
- let fxChoice = parseInt(soundChars.substr(34, 2));
2885
- // [36] 0000-9999 fxParam
2886
- let fxParam = parseInt(soundChars.substr(36, 4));
2887
- // [40] 0000-9999 fxnSteps
2888
- let fxnSteps = parseInt(soundChars.substr(40, 4));
2889
- // Details that encoded randomness to be applied when frame is used:
2890
- // Can the randomness cause any parameters to go out of range?
2891
- // [44] 0000-9999 frequency random
2892
- frequency = applyRandom(frequency, parseInt(soundChars.substr(44, 4)));
2893
- // [48] 0000-9999 end frequency random
2894
- endFrequency = applyRandom(endFrequency, parseInt(soundChars.substr(48, 4)));
2895
- // [52] 0000-9999 volume random
2896
- effectVolume = applyRandom(effectVolume, parseInt(soundChars.substr(52, 4)));
2897
- // [56] 0000-9999 end volume random
2898
- endVolume = applyRandom(endVolume, parseInt(soundChars.substr(56, 4)));
2899
- // [60] 0000-9999 duration random
2900
- duration = applyRandom(duration, parseInt(soundChars.substr(60, 4)));
2901
- // [64] 0000-9999 fxParamRandom
2902
- fxParam = applyRandom(fxParam, parseInt(soundChars.substr(64, 4)));
2903
- // [68] 0000-9999 fxnStepsRandom
2904
- fxnSteps = applyRandom(fxnSteps, parseInt(soundChars.substr(68, 4)));
2905
- if (frequency == -1 || endFrequency == -1 || effectVolume == -1 || endVolume == -1 || duration == -1 || fxParam == -1 || fxnSteps == -1) {
2906
- return false;
2907
- }
2908
- let volumeScaleFactor = 1;
2909
- switch (wave) {
2910
- case 0:
2911
- fx.tone.tonePrint = music.Synthesizer.SineTone;
2912
- break;
2913
- case 1:
2914
- fx.tone.tonePrint = music.Synthesizer.SawtoothTone;
2915
- break;
2916
- case 2:
2917
- fx.tone.tonePrint = music.Synthesizer.TriangleTone;
2918
- break;
2919
- case 3:
2920
- fx.tone.tonePrint = music.Synthesizer.SquareWaveTone;
2921
- break;
2922
- case 4:
2923
- fx.tone.tonePrint = music.Synthesizer.NoiseTone;
2924
- break;
2925
- }
2926
- fx.frequency = frequency;
2927
- fx.duration = duration;
2928
- fx.effects[0].steps = steps;
2929
- switch (shape) {
2930
- case 0:
2931
- fx.effects[0].effect = music.SoundSynthesizerEffects.noInterpolation;
2932
- break;
2933
- case 1:
2934
- fx.effects[0].effect = music.SoundSynthesizerEffects.linearInterpolation;
2935
- fx.effects[0].parameter[0] = endFrequency;
2936
- break;
2937
- case 2:
2938
- fx.effects[0].effect = music.SoundSynthesizerEffects.curveInterpolation;
2939
- fx.effects[0].parameter[0] = endFrequency;
2940
- break;
2941
- case 5:
2942
- fx.effects[0].effect = music.SoundSynthesizerEffects.exponentialRisingInterpolation;
2943
- fx.effects[0].parameter[0] = endFrequency;
2944
- break;
2945
- case 6:
2946
- fx.effects[0].effect = music.SoundSynthesizerEffects.exponentialFallingInterpolation;
2947
- fx.effects[0].parameter[0] = endFrequency;
2948
- break;
2949
- case 8: // various ascending scales - see next switch
2950
- case 10:
2951
- case 12:
2952
- case 14:
2953
- case 16:
2954
- fx.effects[0].effect = music.SoundSynthesizerEffects.appregrioAscending;
2955
- break;
2956
- case 9: // various descending scales - see next switch
2957
- case 11:
2958
- case 13:
2959
- case 15:
2960
- case 17:
2961
- fx.effects[0].effect = music.SoundSynthesizerEffects.appregrioDescending;
2962
- break;
2963
- case 18:
2964
- fx.effects[0].effect = music.SoundSynthesizerEffects.logarithmicInterpolation;
2965
- break;
2966
- }
2967
- // Scale
2968
- switch (shape) {
2969
- case 8:
2970
- case 9:
2971
- fx.effects[0].parameter_p[0] = music.MusicalProgressions.majorScale;
2972
- break;
2973
- case 10:
2974
- case 11:
2975
- fx.effects[0].parameter_p[0] = music.MusicalProgressions.minorScale;
2976
- break;
2977
- case 12:
2978
- case 13:
2979
- fx.effects[0].parameter_p[0] = music.MusicalProgressions.diminished;
2980
- break;
2981
- case 14:
2982
- case 15:
2983
- fx.effects[0].parameter_p[0] = music.MusicalProgressions.chromatic;
2984
- break;
2985
- case 16:
2986
- case 17:
2987
- fx.effects[0].parameter_p[0] = music.MusicalProgressions.wholeTone;
2988
- break;
2989
- }
2990
- // Volume envelope
2991
- let effectVolumeFloat = CLAMP(0, effectVolume, 1023) / 1023.0;
2992
- let endVolumeFloat = CLAMP(0, endVolume, 1023) / 1023.0;
2993
- fx.volume = volumeScaleFactor * effectVolumeFloat;
2994
- fx.effects[1].effect = music.SoundSynthesizerEffects.volumeRampEffect;
2995
- fx.effects[1].steps = 36;
2996
- fx.effects[1].parameter[0] = volumeScaleFactor * endVolumeFloat;
2997
- // Vibrato effect
2998
- // Steps need to be spread across duration evenly.
2999
- let normalizedFxnSteps = (fx.duration / 10000) * fxnSteps;
3000
- switch (fxChoice) {
3001
- case 1:
3002
- fx.effects[2].steps = normalizedFxnSteps;
3003
- fx.effects[2].effect = music.SoundSynthesizerEffects.frequencyVibratoEffect;
3004
- fx.effects[2].parameter[0] = fxParam;
3005
- break;
3006
- case 2:
3007
- fx.effects[2].steps = normalizedFxnSteps;
3008
- fx.effects[2].effect = music.SoundSynthesizerEffects.volumeVibratoEffect;
3009
- fx.effects[2].parameter[0] = fxParam;
3010
- break;
3011
- case 3:
3012
- fx.effects[2].steps = normalizedFxnSteps;
3013
- fx.effects[2].effect = music.SoundSynthesizerEffects.warbleInterpolation;
3014
- fx.effects[2].parameter[0] = fxParam;
3015
- break;
3016
- }
3017
- return true;
3018
- }
3019
- music.parseSoundExpression = parseSoundExpression;
3020
- function random(max) {
3021
- return Math.floor(Math.random() * max);
3022
- }
3023
- function CLAMP(min, value, max) {
3024
- return Math.min(max, Math.max(min, value));
3025
- }
3026
- function applyRandom(value, rand) {
3027
- if (value < 0 || rand < 0) {
3028
- return -1;
3029
- }
3030
- const delta = random(rand * 2 + 1) - rand;
3031
- return Math.abs(value + delta);
3032
- }
3033
- function blankSoundEffect() {
3034
- const res = {
3035
- frequency: 0,
3036
- volume: 1,
3037
- duration: 0,
3038
- tone: {
3039
- tonePrint: undefined,
3040
- parameter: [0]
3041
- },
3042
- effects: []
3043
- };
3044
- for (let i = 0; i < music.EMOJI_SYNTHESIZER_TONE_EFFECTS; i++) {
3045
- res.effects.push({
3046
- effect: undefined,
3047
- step: 0,
3048
- steps: 0,
3049
- parameter: [],
3050
- parameter_p: []
3051
- });
3052
- }
3053
- return res;
3054
- }
3055
- function delayAsync(millis) {
3056
- return new Promise(resolve => setTimeout(resolve, millis));
2378
+ const giggle = "giggle";
2379
+ const giggleData = "010230988019008440044008881023001601003300240000000000000000000000000000,110232570087411440044008880352005901003300010000000000000000010000000000,310232729021105440288908880091006300000000240700020000000000003000000000,310232729010205440288908880091006300000000240700020000000000003000000000,310232729011405440288908880091006300000000240700020000000000003000000000";
2380
+ const happy = "happy";
2381
+ const happyData = "010231992066911440044008880262002800001800020500000000000000010000000000,002322129029508440240408880000000400022400110000000000000000007500000000,000002129029509440240408880145000400022400110000000000000000007500000000";
2382
+ const hello = "hello";
2383
+ const helloData = "310230673019702440118708881023012800000000240000000000000000000000000000,300001064001602440098108880000012800000100040000000000000000000000000000,310231064029302440098108881023012800000100040000000000000000000000000000";
2384
+ const mysterious = "mysterious";
2385
+ const mysteriousData = "400002390033100440240408880477000400022400110400000000000000008000000000,405512845385000440044008880000012803010500160000000000000000085000500015";
2386
+ const sad = "sad";
2387
+ const sadData = "310232226070801440162408881023012800000100240000000000000000000000000000,310231623093602440093908880000012800000100240000000000000000000000000000";
2388
+ const slide = "slide";
2389
+ const slideData = "105202325022302440240408881023012801020000110400000000000000010000000000,010232520091002440044008881023012801022400110400000000000000010000000000";
2390
+ const soaring = "soaring";
2391
+ const soaringData = "210234009530905440599908881023002202000400020250000000000000020000000000,402233727273014440044008880000003101024400030000000000000000000000000000";
2392
+ const spring = "spring";
2393
+ const springData = "306590037116312440058708880807003400000000240000000000000000050000000000,010230037116313440058708881023003100000000240000000000000000050000000000";
2394
+ const twinkle = "twinkle";
2395
+ const twinkleData = "010180007672209440075608880855012800000000240000000000000000000000000000";
2396
+ const yawn = "yawn";
2397
+ const yawnData = "200002281133202440150008881023012801024100240400030000000000010000000000,005312520091002440044008880636012801022400110300000000000000010000000000,008220784019008440044008880681001600005500240000000000000000005000000000,004790784019008440044008880298001600000000240000000000000000005000000000,003210784019008440044008880108001600003300080000000000000000005000000000";
2398
+ function lookupBuiltIn(sound) {
2399
+ if (sound == giggle)
2400
+ return giggleData;
2401
+ if (sound == happy)
2402
+ return happyData;
2403
+ if (sound == hello)
2404
+ return helloData;
2405
+ if (sound == mysterious)
2406
+ return mysteriousData;
2407
+ if (sound == sad)
2408
+ return sadData;
2409
+ if (sound == slide)
2410
+ return slideData;
2411
+ if (sound == soaring)
2412
+ return soaringData;
2413
+ if (sound == spring)
2414
+ return springData;
2415
+ if (sound == twinkle)
2416
+ return twinkleData;
2417
+ if (sound == yawn)
2418
+ return yawnData;
2419
+ return sound;
3057
2420
  }
3058
2421
  })(music = pxsim.music || (pxsim.music = {}));
3059
2422
  })(pxsim || (pxsim = {}));
@@ -3817,7 +3180,7 @@ path.sim-board {
3817
3180
  let pt = this.element.createSVGPoint();
3818
3181
  pxsim.svg.buttonEvents(this.head, (ev) => {
3819
3182
  let cur = pxsim.svg.cursorPoint(pt, this.element, ev);
3820
- state.compassState.heading = 360 - (Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI) + 90) - 360;
3183
+ state.compassState.heading = Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI) + 90;
3821
3184
  if (state.compassState.heading < 0)
3822
3185
  state.compassState.heading += 360;
3823
3186
  this.updateHeading();
@@ -3826,7 +3189,7 @@ path.sim-board {
3826
3189
  }
3827
3190
  let txt = state.compassState.heading.toString() + "°";
3828
3191
  if (txt != this.headText.textContent) {
3829
- pxsim.svg.rotateElement(this.head, xc, yc, 360 - state.compassState.heading - 180);
3192
+ pxsim.svg.rotateElement(this.head, xc, yc, state.compassState.heading - 180);
3830
3193
  this.headText.textContent = txt;
3831
3194
  if (this.props.runtime)
3832
3195
  this.props.runtime.environmentGlobals[pxsim.localization.lf("heading")] = state.compassState.heading;