pxt-core 8.2.2 → 8.2.5
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/backendutils.js +22 -4
- package/built/gdb.js +3 -3
- package/built/pxt-common.json +1 -1
- package/built/pxt.js +377 -109
- package/built/pxtblockly.js +21 -4
- package/built/pxtblocks.d.ts +1 -0
- package/built/pxtblocks.js +21 -4
- package/built/pxtcompiler.d.ts +1 -0
- package/built/pxtcompiler.js +34 -8
- package/built/pxteditor.d.ts +1 -0
- package/built/pxteditor.js +2 -1
- package/built/pxtlib.d.ts +15 -0
- package/built/pxtlib.js +219 -15
- package/built/pxtsim.d.ts +1 -1
- package/built/pxtsim.js +124 -86
- package/built/target.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtblockly.js +1 -1
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtcompiler.js +1 -1
- package/built/web/pxteditor.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtsim.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/rtlsemantic.css +1 -1
- package/built/web/semantic.css +1 -1
- package/package.json +1 -1
- package/theme/asset-editor.less +5 -0
- package/theme/greenscreen.less +4 -1
- package/theme/home.less +0 -5
- package/theme/tutorial-sidebar.less +42 -1
- package/theme/tutorial.less +8 -0
package/built/pxtlib.js
CHANGED
|
@@ -8713,14 +8713,32 @@ var pxt;
|
|
|
8713
8713
|
docs.prepTemplate = prepTemplate;
|
|
8714
8714
|
function setupRenderer(renderer) {
|
|
8715
8715
|
renderer.image = function (href, title, text) {
|
|
8716
|
-
|
|
8716
|
+
var _a, _b;
|
|
8717
|
+
const endpointName = "makecodeprodmediaeastus-usea";
|
|
8717
8718
|
if (href.startsWith("youtube:")) {
|
|
8718
|
-
let out = '<div class="tutorial-video-embed"><iframe src="https://www.youtube.com/embed/' + href.split(":").pop()
|
|
8719
|
-
+ '" title="' +
|
|
8719
|
+
let out = '<div class="tutorial-video-embed"><iframe class="yt-embed" src="https://www.youtube.com/embed/' + href.split(":").pop()
|
|
8720
|
+
+ '" title="' + text + '" frameborder="0" ' + 'allowFullScreen ' + 'allow="autoplay; picture-in-picture"></iframe></div>';
|
|
8720
8721
|
return out;
|
|
8721
8722
|
}
|
|
8722
8723
|
else if (href.startsWith("azuremedia:")) {
|
|
8723
|
-
let
|
|
8724
|
+
let videoID = href.split(":")[1];
|
|
8725
|
+
const flagsSplit = videoID.split("?");
|
|
8726
|
+
let startTime;
|
|
8727
|
+
let endTime;
|
|
8728
|
+
if (flagsSplit[1]) {
|
|
8729
|
+
videoID = flagsSplit[0];
|
|
8730
|
+
const passedParameters = flagsSplit[1];
|
|
8731
|
+
startTime = (_a = /start(?:time)?=(\d+)/i.exec(passedParameters)) === null || _a === void 0 ? void 0 : _a[1];
|
|
8732
|
+
endTime = (_b = /end(?:time)?=(\d+)/i.exec(passedParameters)) === null || _b === void 0 ? void 0 : _b[1];
|
|
8733
|
+
}
|
|
8734
|
+
const url = new URL(`https://${endpointName}.streaming.media.azure.net/${videoID}/manifest(format=mpd-time-csf).mpd`);
|
|
8735
|
+
if (startTime) {
|
|
8736
|
+
url.hash = `t=${startTime}`;
|
|
8737
|
+
}
|
|
8738
|
+
if (endTime) {
|
|
8739
|
+
url.searchParams.append("endTime", endTime);
|
|
8740
|
+
}
|
|
8741
|
+
let out = `<div class="tutorial-video-embed"><video class="ams-embed" controls src="${url.toString()}" /></div>`;
|
|
8724
8742
|
return out;
|
|
8725
8743
|
}
|
|
8726
8744
|
else {
|
|
@@ -10500,14 +10518,17 @@ var pxt;
|
|
|
10500
10518
|
return false;
|
|
10501
10519
|
}
|
|
10502
10520
|
function isRepoBanned(repo, config) {
|
|
10521
|
+
var _a, _b;
|
|
10503
10522
|
if (isOrgBanned(repo, config))
|
|
10504
10523
|
return true;
|
|
10505
10524
|
if (!config)
|
|
10506
10525
|
return false; // don't know
|
|
10507
|
-
if (!repo
|
|
10526
|
+
if (!repo)
|
|
10508
10527
|
return true;
|
|
10528
|
+
const repoFull = (_a = repo.fullName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
10529
|
+
const repoSlug = (_b = repo.slug) === null || _b === void 0 ? void 0 : _b.toLowerCase();
|
|
10509
10530
|
if (config.bannedRepos
|
|
10510
|
-
&& config.bannedRepos.some(fn => fn.toLowerCase() ==
|
|
10531
|
+
&& config.bannedRepos.some(fn => fn && (fn.toLowerCase() == repoFull || fn.toLowerCase() == repoSlug)))
|
|
10511
10532
|
return true;
|
|
10512
10533
|
return false;
|
|
10513
10534
|
}
|
|
@@ -10521,13 +10542,15 @@ var pxt;
|
|
|
10521
10542
|
return false;
|
|
10522
10543
|
}
|
|
10523
10544
|
function isRepoApproved(repo, config) {
|
|
10524
|
-
var _a;
|
|
10545
|
+
var _a, _b;
|
|
10525
10546
|
if (isOrgApproved(repo, config))
|
|
10526
10547
|
return true;
|
|
10527
|
-
|
|
10548
|
+
const repoFull = (_a = repo === null || repo === void 0 ? void 0 : repo.fullName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
10549
|
+
const repoSlug = (_b = repo === null || repo === void 0 ? void 0 : repo.slug) === null || _b === void 0 ? void 0 : _b.toLowerCase();
|
|
10550
|
+
if (!(config === null || config === void 0 ? void 0 : config.approvedRepoLib) || !(repoFull || repoSlug))
|
|
10528
10551
|
return false;
|
|
10529
|
-
if (
|
|
10530
|
-
|
|
10552
|
+
if (config.approvedRepoLib[repoFull]
|
|
10553
|
+
|| config.approvedRepoLib[repoSlug])
|
|
10531
10554
|
return true;
|
|
10532
10555
|
return false;
|
|
10533
10556
|
}
|
|
@@ -14270,7 +14293,7 @@ var pxt;
|
|
|
14270
14293
|
// and pulls from master
|
|
14271
14294
|
const modtag = (modid === null || modid === void 0 ? void 0 : modid.tag) || ((_a = mod.config) === null || _a === void 0 ? void 0 : _a.version);
|
|
14272
14295
|
const vertag = verid.tag;
|
|
14273
|
-
// if there is no tag on the current dependency,
|
|
14296
|
+
// if there is no tag on the current dependency,
|
|
14274
14297
|
// assume same as existing module version if any
|
|
14275
14298
|
if (modtag && !vertag) {
|
|
14276
14299
|
pxt.debug(`unversioned ${ver}, using ${modtag}`);
|
|
@@ -14729,6 +14752,8 @@ var pxt;
|
|
|
14729
14752
|
opts.otherMultiVariants.push(etarget);
|
|
14730
14753
|
}
|
|
14731
14754
|
else {
|
|
14755
|
+
etarget.target.isNative = opts.target.isNative;
|
|
14756
|
+
opts.target = etarget.target;
|
|
14732
14757
|
ext = einfo;
|
|
14733
14758
|
opts.otherMultiVariants = [];
|
|
14734
14759
|
}
|
|
@@ -15684,7 +15709,12 @@ var ts;
|
|
|
15684
15709
|
const left = param.substr(0, dotIdx);
|
|
15685
15710
|
let right = param.substr(dotIdx + 1);
|
|
15686
15711
|
right = pxtc.U.snakify(right).toUpperCase();
|
|
15687
|
-
|
|
15712
|
+
if (left) {
|
|
15713
|
+
return `${left}.${right}`;
|
|
15714
|
+
}
|
|
15715
|
+
else {
|
|
15716
|
+
return right;
|
|
15717
|
+
}
|
|
15688
15718
|
}
|
|
15689
15719
|
return param;
|
|
15690
15720
|
}
|
|
@@ -16905,6 +16935,97 @@ var pxt;
|
|
|
16905
16935
|
return outParts.join(" ");
|
|
16906
16936
|
}
|
|
16907
16937
|
}
|
|
16938
|
+
function soundToInstructionBuffer(sound, fxSteps, fxRange) {
|
|
16939
|
+
const { startFrequency, endFrequency, startVolume, endVolume, interpolation, duration } = sound;
|
|
16940
|
+
const steps = [];
|
|
16941
|
+
// Optimize the simple case
|
|
16942
|
+
if (sound.interpolation === "linear" && sound.effect === "none") {
|
|
16943
|
+
steps.push({
|
|
16944
|
+
frequency: startFrequency,
|
|
16945
|
+
volume: (startVolume / assets.MAX_VOLUME) * 1024,
|
|
16946
|
+
});
|
|
16947
|
+
steps.push({
|
|
16948
|
+
frequency: endFrequency,
|
|
16949
|
+
volume: (endVolume / assets.MAX_VOLUME) * 1024,
|
|
16950
|
+
});
|
|
16951
|
+
}
|
|
16952
|
+
else {
|
|
16953
|
+
fxSteps = Math.min(fxSteps, Math.floor(duration / 5));
|
|
16954
|
+
const getVolumeAt = (t) => ((startVolume + t * (endVolume - startVolume) / duration) / assets.MAX_VOLUME) * 1024;
|
|
16955
|
+
let getFrequencyAt;
|
|
16956
|
+
switch (interpolation) {
|
|
16957
|
+
case "linear":
|
|
16958
|
+
getFrequencyAt = t => startFrequency + t * (endFrequency - startFrequency) / duration;
|
|
16959
|
+
break;
|
|
16960
|
+
case "curve":
|
|
16961
|
+
getFrequencyAt = t => startFrequency + (endFrequency - startFrequency) * Math.sin(t / duration * (Math.PI / 2));
|
|
16962
|
+
break;
|
|
16963
|
+
case "logarithmic":
|
|
16964
|
+
getFrequencyAt = t => startFrequency + Math.log10(1 + 9 * (t / duration)) * (endFrequency - startFrequency);
|
|
16965
|
+
break;
|
|
16966
|
+
}
|
|
16967
|
+
const timeSlice = duration / fxSteps;
|
|
16968
|
+
for (let i = 0; i < fxSteps; i++) {
|
|
16969
|
+
const newStep = {
|
|
16970
|
+
frequency: Math.max(getFrequencyAt(i * timeSlice), 1),
|
|
16971
|
+
volume: getVolumeAt(i * timeSlice)
|
|
16972
|
+
};
|
|
16973
|
+
if (sound.effect === "tremolo") {
|
|
16974
|
+
if (i % 2 === 0) {
|
|
16975
|
+
newStep.volume = Math.max(newStep.volume - fxRange * 500, 0);
|
|
16976
|
+
}
|
|
16977
|
+
else {
|
|
16978
|
+
newStep.volume = Math.min(newStep.volume + fxRange * 500, 1023);
|
|
16979
|
+
}
|
|
16980
|
+
}
|
|
16981
|
+
else if (sound.effect === "vibrato") {
|
|
16982
|
+
if (i % 2 === 0) {
|
|
16983
|
+
newStep.frequency = Math.max(newStep.frequency - fxRange * 100, 1);
|
|
16984
|
+
}
|
|
16985
|
+
else {
|
|
16986
|
+
newStep.frequency = newStep.frequency + fxRange * 100;
|
|
16987
|
+
}
|
|
16988
|
+
}
|
|
16989
|
+
else if (sound.effect === "warble") {
|
|
16990
|
+
if (i % 2 === 0) {
|
|
16991
|
+
newStep.frequency = Math.max(newStep.frequency - fxRange * 1000, 1);
|
|
16992
|
+
}
|
|
16993
|
+
else {
|
|
16994
|
+
newStep.frequency = newStep.frequency + fxRange * 1000;
|
|
16995
|
+
}
|
|
16996
|
+
}
|
|
16997
|
+
steps.push(newStep);
|
|
16998
|
+
}
|
|
16999
|
+
}
|
|
17000
|
+
const out = new Uint8Array(12 * (steps.length - 1));
|
|
17001
|
+
const stepDuration = Math.floor(duration / (steps.length - 1));
|
|
17002
|
+
for (let i = 0; i < steps.length - 1; i++) {
|
|
17003
|
+
const offset = i * 12;
|
|
17004
|
+
out[offset] = waveToValue(sound.wave);
|
|
17005
|
+
set16BitNumber(out, offset + 2, steps[i].frequency);
|
|
17006
|
+
set16BitNumber(out, offset + 4, stepDuration);
|
|
17007
|
+
set16BitNumber(out, offset + 6, steps[i].volume);
|
|
17008
|
+
set16BitNumber(out, offset + 8, steps[i + 1].volume);
|
|
17009
|
+
set16BitNumber(out, offset + 10, steps[i + 1].frequency);
|
|
17010
|
+
}
|
|
17011
|
+
return out;
|
|
17012
|
+
}
|
|
17013
|
+
assets.soundToInstructionBuffer = soundToInstructionBuffer;
|
|
17014
|
+
function waveToValue(wave) {
|
|
17015
|
+
switch (wave) {
|
|
17016
|
+
case "square": return 15;
|
|
17017
|
+
case "sine": return 3;
|
|
17018
|
+
case "triangle": return 1;
|
|
17019
|
+
case "noise": return 18;
|
|
17020
|
+
case "sawtooth": return 2;
|
|
17021
|
+
}
|
|
17022
|
+
}
|
|
17023
|
+
function set16BitNumber(buf, offset, value) {
|
|
17024
|
+
const temp = new Uint8Array(2);
|
|
17025
|
+
new Uint16Array(temp.buffer)[0] = value | 0;
|
|
17026
|
+
buf[offset] = temp[0];
|
|
17027
|
+
buf[offset + 1] = temp[1];
|
|
17028
|
+
}
|
|
16908
17029
|
})(assets = pxt.assets || (pxt.assets = {}));
|
|
16909
17030
|
})(pxt || (pxt = {}));
|
|
16910
17031
|
// See https://github.com/microsoft/TouchDevelop-backend/blob/master/docs/streams.md
|
|
@@ -21507,6 +21628,18 @@ var ts;
|
|
|
21507
21628
|
}
|
|
21508
21629
|
}
|
|
21509
21630
|
assembler.Line = Line;
|
|
21631
|
+
const MAX_OBJ_USERS = 5;
|
|
21632
|
+
class AsmObject {
|
|
21633
|
+
constructor(id, description) {
|
|
21634
|
+
this.id = id;
|
|
21635
|
+
this.description = description;
|
|
21636
|
+
this.sizeAdj = 0;
|
|
21637
|
+
this.users = [];
|
|
21638
|
+
}
|
|
21639
|
+
get size() {
|
|
21640
|
+
return (this.endLocation - this.startLocation) - this.sizeAdj;
|
|
21641
|
+
}
|
|
21642
|
+
}
|
|
21510
21643
|
// File is the center of the action: parsing a file into a sequence of Lines
|
|
21511
21644
|
// and also emitting the binary (buf)
|
|
21512
21645
|
class File {
|
|
@@ -21531,6 +21664,11 @@ var ts;
|
|
|
21531
21664
|
this.throwOnError = false;
|
|
21532
21665
|
this.disablePeepHole = false;
|
|
21533
21666
|
this.stackAtLabel = {};
|
|
21667
|
+
this.codeSizeStats = false;
|
|
21668
|
+
this.labelToObject = {};
|
|
21669
|
+
this.idToObject = {};
|
|
21670
|
+
this.objSuspendStart = 0;
|
|
21671
|
+
this.labelsToObjectDone = false;
|
|
21534
21672
|
this.currLine = new Line(this, "<start>");
|
|
21535
21673
|
this.currLine.lineNo = 0;
|
|
21536
21674
|
this.ei = ei;
|
|
@@ -21550,6 +21688,15 @@ var ts;
|
|
|
21550
21688
|
pc() {
|
|
21551
21689
|
return this.location() + this.baseOffset;
|
|
21552
21690
|
}
|
|
21691
|
+
useLabel(name) {
|
|
21692
|
+
if (!this.currObject || name[0] == '.' || this.objSuspendStart)
|
|
21693
|
+
return;
|
|
21694
|
+
const obj = pxtc.U.lookup(this.labelToObject, name);
|
|
21695
|
+
if (!obj || obj == this.currObject)
|
|
21696
|
+
return;
|
|
21697
|
+
if (obj.users.length < MAX_OBJ_USERS && obj.users.indexOf(this.currObject) < 0)
|
|
21698
|
+
obj.users.push(this.currObject);
|
|
21699
|
+
}
|
|
21553
21700
|
// parsing of an "integer", well actually much more than
|
|
21554
21701
|
// just that
|
|
21555
21702
|
parseOneInt(s) {
|
|
@@ -21679,6 +21826,7 @@ var ts;
|
|
|
21679
21826
|
return name;
|
|
21680
21827
|
}
|
|
21681
21828
|
lookupLabel(name, direct = false) {
|
|
21829
|
+
this.useLabel(name);
|
|
21682
21830
|
let v = null;
|
|
21683
21831
|
let scoped = this.scopedName(name);
|
|
21684
21832
|
if (this.labels.hasOwnProperty(scoped)) {
|
|
@@ -21837,6 +21985,37 @@ var ts;
|
|
|
21837
21985
|
};
|
|
21838
21986
|
let num0;
|
|
21839
21987
|
switch (words[0]) {
|
|
21988
|
+
case ".object":
|
|
21989
|
+
if (!this.codeSizeStats) {
|
|
21990
|
+
// do nothing
|
|
21991
|
+
}
|
|
21992
|
+
else if (words[1] == "PUSH") {
|
|
21993
|
+
this.objSuspendStart = this.location();
|
|
21994
|
+
}
|
|
21995
|
+
else if (words[1] == "POP") {
|
|
21996
|
+
if (this.objSuspendStart)
|
|
21997
|
+
this.currObject.sizeAdj += this.location() - this.objSuspendStart;
|
|
21998
|
+
this.objSuspendStart = 0;
|
|
21999
|
+
}
|
|
22000
|
+
else {
|
|
22001
|
+
if (this.currObject)
|
|
22002
|
+
this.currObject.endLocation = this.location();
|
|
22003
|
+
this.currObject = pxtc.U.lookup(this.idToObject, words[1]);
|
|
22004
|
+
if (!this.currObject) {
|
|
22005
|
+
const str = l.text.replace(/^[^"]*/, "");
|
|
22006
|
+
let parsed = words[1];
|
|
22007
|
+
if (words.length > 2) {
|
|
22008
|
+
parsed = parseString(str.trim());
|
|
22009
|
+
if (parsed == null)
|
|
22010
|
+
this.directiveError(lf("expecting string in .object"));
|
|
22011
|
+
}
|
|
22012
|
+
this.currObject = new AsmObject(words[1], parsed);
|
|
22013
|
+
this.idToObject[words[1]] = this.currObject;
|
|
22014
|
+
}
|
|
22015
|
+
this.currObject.sizeAdj = 0;
|
|
22016
|
+
this.currObject.startLocation = this.location();
|
|
22017
|
+
}
|
|
22018
|
+
break;
|
|
21840
22019
|
case ".ascii":
|
|
21841
22020
|
case ".asciz":
|
|
21842
22021
|
case ".string":
|
|
@@ -22031,6 +22210,8 @@ var ts;
|
|
|
22031
22210
|
}
|
|
22032
22211
|
}
|
|
22033
22212
|
handleOneInstruction(ln, instr) {
|
|
22213
|
+
if (this.codeSizeStats && ln.ldlitLabel)
|
|
22214
|
+
this.useLabel(ln.ldlitLabel);
|
|
22034
22215
|
let op = instr.emit(ln);
|
|
22035
22216
|
if (!op.error) {
|
|
22036
22217
|
this.stack += op.stack;
|
|
@@ -22139,6 +22320,8 @@ var ts;
|
|
|
22139
22320
|
if (l.words.length == 0)
|
|
22140
22321
|
return;
|
|
22141
22322
|
if (l.type == "label") {
|
|
22323
|
+
if (this.currObject && !this.labelsToObjectDone && l.words[0][0] != '.')
|
|
22324
|
+
this.labelToObject[l.words[0]] = this.currObject;
|
|
22142
22325
|
let lblname = this.scopedName(l.words[0]);
|
|
22143
22326
|
this.prevLabel = lblname;
|
|
22144
22327
|
if (this.finalEmit) {
|
|
@@ -22179,6 +22362,8 @@ var ts;
|
|
|
22179
22362
|
pxtc.oops();
|
|
22180
22363
|
}
|
|
22181
22364
|
});
|
|
22365
|
+
this.labelsToObjectDone = true;
|
|
22366
|
+
this.currObject = null;
|
|
22182
22367
|
}
|
|
22183
22368
|
getSourceMap() {
|
|
22184
22369
|
const sourceMap = {};
|
|
@@ -22211,6 +22396,22 @@ var ts;
|
|
|
22211
22396
|
}
|
|
22212
22397
|
return sourceMap;
|
|
22213
22398
|
}
|
|
22399
|
+
getCodeSizeStats() {
|
|
22400
|
+
if (!this.codeSizeStats)
|
|
22401
|
+
return "";
|
|
22402
|
+
const objs = pxtc.U.values(this.idToObject);
|
|
22403
|
+
objs.sort((a, b) => b.size - a.size);
|
|
22404
|
+
let r = ";\n; Code size:\n;\n";
|
|
22405
|
+
for (const obj of objs) {
|
|
22406
|
+
r += `; ${(" " + obj.size).slice(-6)} ${obj.description} [${obj.id}]\n`;
|
|
22407
|
+
if (obj.users.length >= MAX_OBJ_USERS)
|
|
22408
|
+
r += `; by many, including ${obj.users[0].description}\n`;
|
|
22409
|
+
else
|
|
22410
|
+
for (const x of obj.users)
|
|
22411
|
+
r += `; by ${x.description} [${x.id}]\n`;
|
|
22412
|
+
}
|
|
22413
|
+
return r;
|
|
22414
|
+
}
|
|
22214
22415
|
getSource(clean, numStmts = 1, flashSize = 0) {
|
|
22215
22416
|
let lenPrev = 0;
|
|
22216
22417
|
let size = (lbl) => {
|
|
@@ -22226,8 +22427,11 @@ var ts;
|
|
|
22226
22427
|
let lenLiterals = size("_literals_end");
|
|
22227
22428
|
let lenAllCode = lenPrev;
|
|
22228
22429
|
let totalSize = (lenTotal + this.baseOffset) & 0xffffff;
|
|
22229
|
-
if (flashSize && totalSize > flashSize)
|
|
22230
|
-
|
|
22430
|
+
if (flashSize && totalSize > flashSize) {
|
|
22431
|
+
const e = new Error(lf("program too big by {0} bytes!", totalSize - flashSize));
|
|
22432
|
+
e.ksErrorCode = 9283;
|
|
22433
|
+
throw e;
|
|
22434
|
+
}
|
|
22231
22435
|
flashSize = flashSize || 128 * 1024;
|
|
22232
22436
|
let totalInfo = lf("; total bytes: {0} ({1}% of {2}k flash with {3} free)", totalSize, (100 * totalSize / flashSize).toFixed(1), (flashSize / 1024).toFixed(1), flashSize - totalSize);
|
|
22233
22437
|
let res =
|
|
@@ -22235,7 +22439,7 @@ var ts;
|
|
|
22235
22439
|
lf("; generated code sizes (bytes): {0} (incl. {1} user, {2} helpers, {3} vtables, {4} lits); src size {5}\n", lenAllCode, lenCode, lenHelpers, lenVtables, lenLiterals, lenTotal - lenAllCode) +
|
|
22236
22440
|
lf("; assembly: {0} lines; density: {1} bytes/stmt; ({2} stmts)\n", this.lines.length, Math.round(100 * lenCode / numStmts) / 100, numStmts) +
|
|
22237
22441
|
totalInfo + "\n" +
|
|
22238
|
-
this.stats + "\n\n";
|
|
22442
|
+
this.stats + this.getCodeSizeStats() + "\n\n";
|
|
22239
22443
|
let skipOne = false;
|
|
22240
22444
|
this.lines.forEach((ln, i) => {
|
|
22241
22445
|
if (ln.words[0] == "_stored_program") {
|
package/built/pxtsim.d.ts
CHANGED
|
@@ -1424,11 +1424,11 @@ declare namespace pxsim {
|
|
|
1424
1424
|
function frequency(): number;
|
|
1425
1425
|
function muteAllChannels(): void;
|
|
1426
1426
|
function queuePlayInstructions(when: number, b: RefBuffer): void;
|
|
1427
|
-
function playInstructionsAsync(b: RefBuffer): Promise<void>;
|
|
1428
1427
|
function tone(frequency: number, gain: number): void;
|
|
1429
1428
|
function setCurrentToneGain(gain: number): void;
|
|
1430
1429
|
function playBufferAsync(buf: RefBuffer): Promise<void>;
|
|
1431
1430
|
function playPCMBufferStreamAsync(pull: () => Float32Array, sampleRate: number, volume?: number, isCancelled?: () => boolean): Promise<void>;
|
|
1431
|
+
function playInstructionsAsync(instructions: Uint8Array, isCancelled?: () => boolean, onPull?: (freq: number, volume: number) => void): Promise<void>;
|
|
1432
1432
|
function sendMidiMessage(buf: RefBuffer): void;
|
|
1433
1433
|
}
|
|
1434
1434
|
interface IPointerEvents {
|
package/built/pxtsim.js
CHANGED
|
@@ -6295,7 +6295,7 @@ var pxsim;
|
|
|
6295
6295
|
}
|
|
6296
6296
|
}
|
|
6297
6297
|
createFrame(url) {
|
|
6298
|
-
var _a;
|
|
6298
|
+
var _a, _b;
|
|
6299
6299
|
const wrapper = document.createElement("div");
|
|
6300
6300
|
wrapper.className = `simframe ui embed`;
|
|
6301
6301
|
const frame = document.createElement('iframe');
|
|
@@ -6306,7 +6306,7 @@ var pxsim;
|
|
|
6306
6306
|
frame.setAttribute('sandbox', 'allow-same-origin allow-scripts');
|
|
6307
6307
|
frame.className = 'no-select';
|
|
6308
6308
|
let furl = url || this.getSimUrl().toString();
|
|
6309
|
-
if (this._runOptions.hideSimButtons) {
|
|
6309
|
+
if ((_a = this._runOptions) === null || _a === void 0 ? void 0 : _a.hideSimButtons) {
|
|
6310
6310
|
const urlObject = new URL(furl);
|
|
6311
6311
|
urlObject.searchParams.append("hideSimButtons", "1");
|
|
6312
6312
|
furl = urlObject.toString();
|
|
@@ -6316,7 +6316,7 @@ var pxsim;
|
|
|
6316
6316
|
frame.frameBorder = "0";
|
|
6317
6317
|
frame.dataset['runid'] = this.runId;
|
|
6318
6318
|
frame.dataset['origin'] = new URL(furl).origin || "*";
|
|
6319
|
-
if ((
|
|
6319
|
+
if ((_b = this._runOptions) === null || _b === void 0 ? void 0 : _b.autofocus)
|
|
6320
6320
|
frame.setAttribute("autofocus", "true");
|
|
6321
6321
|
wrapper.appendChild(frame);
|
|
6322
6322
|
const i = document.createElement("i");
|
|
@@ -7250,92 +7250,10 @@ var pxsim;
|
|
|
7250
7250
|
.then(() => {
|
|
7251
7251
|
if (prevStop != instrStopId)
|
|
7252
7252
|
return Promise.resolve();
|
|
7253
|
-
return playInstructionsAsync(b);
|
|
7253
|
+
return playInstructionsAsync(b.data);
|
|
7254
7254
|
});
|
|
7255
7255
|
}
|
|
7256
7256
|
AudioContextManager.queuePlayInstructions = queuePlayInstructions;
|
|
7257
|
-
function playInstructionsAsync(b) {
|
|
7258
|
-
const prevStop = instrStopId;
|
|
7259
|
-
let ctx = context();
|
|
7260
|
-
let idx = 0;
|
|
7261
|
-
let ch = new Channel();
|
|
7262
|
-
let currWave = -1;
|
|
7263
|
-
let currFreq = -1;
|
|
7264
|
-
let timeOff = 0;
|
|
7265
|
-
if (channels.length > 5)
|
|
7266
|
-
channels[0].remove();
|
|
7267
|
-
channels.push(ch);
|
|
7268
|
-
/** Square waves are perceved as much louder than other sounds, so scale it down a bit to make it less jarring **/
|
|
7269
|
-
const scaleVol = (n, isSqWave) => (n / 1024) / 4 * (isSqWave ? .5 : 1);
|
|
7270
|
-
const finish = () => {
|
|
7271
|
-
ch.disconnectNodes();
|
|
7272
|
-
timeOff = 0;
|
|
7273
|
-
currWave = -1;
|
|
7274
|
-
currFreq = -1;
|
|
7275
|
-
};
|
|
7276
|
-
const loopAsync = () => {
|
|
7277
|
-
if (idx >= b.data.length || !b.data[idx])
|
|
7278
|
-
return pxsim.U.delay(timeOff).then(finish);
|
|
7279
|
-
const soundWaveIdx = b.data[idx];
|
|
7280
|
-
const freq = pxsim.BufferMethods.getNumber(b, pxsim.BufferMethods.NumberFormat.UInt16LE, idx + 2);
|
|
7281
|
-
const duration = pxsim.BufferMethods.getNumber(b, pxsim.BufferMethods.NumberFormat.UInt16LE, idx + 4);
|
|
7282
|
-
const startVol = pxsim.BufferMethods.getNumber(b, pxsim.BufferMethods.NumberFormat.UInt16LE, idx + 6);
|
|
7283
|
-
const endVol = pxsim.BufferMethods.getNumber(b, pxsim.BufferMethods.NumberFormat.UInt16LE, idx + 8);
|
|
7284
|
-
const endFreq = pxsim.BufferMethods.getNumber(b, pxsim.BufferMethods.NumberFormat.UInt16LE, idx + 10);
|
|
7285
|
-
const isSquareWave = 11 <= soundWaveIdx && soundWaveIdx <= 15;
|
|
7286
|
-
const isFilteredNoise = soundWaveIdx == 4 || (16 <= soundWaveIdx && soundWaveIdx <= 18);
|
|
7287
|
-
const scaledStart = scaleVol(startVol, isSquareWave);
|
|
7288
|
-
const scaledEnd = scaleVol(endVol, isSquareWave);
|
|
7289
|
-
if (!ctx || prevStop != instrStopId)
|
|
7290
|
-
return pxsim.U.delay(duration);
|
|
7291
|
-
if (currWave != soundWaveIdx || currFreq != freq || freq != endFreq) {
|
|
7292
|
-
if (ch.generator) {
|
|
7293
|
-
return pxsim.U.delay(timeOff)
|
|
7294
|
-
.then(() => {
|
|
7295
|
-
finish();
|
|
7296
|
-
return loopAsync();
|
|
7297
|
-
});
|
|
7298
|
-
}
|
|
7299
|
-
ch.generator = getGenerator(soundWaveIdx, freq);
|
|
7300
|
-
if (!ch.generator)
|
|
7301
|
-
return pxsim.U.delay(duration);
|
|
7302
|
-
currWave = soundWaveIdx;
|
|
7303
|
-
currFreq = freq;
|
|
7304
|
-
ch.gain = ctx.createGain();
|
|
7305
|
-
ch.gain.gain.value = 0;
|
|
7306
|
-
ch.gain.gain.setTargetAtTime(scaledStart, _context.currentTime, 0.015);
|
|
7307
|
-
if (endFreq != freq) {
|
|
7308
|
-
if (ch.generator.frequency != undefined) {
|
|
7309
|
-
// If generator is an OscillatorNode
|
|
7310
|
-
const param = ch.generator.frequency;
|
|
7311
|
-
param.linearRampToValueAtTime(endFreq, ctx.currentTime + ((timeOff + duration) / 1000));
|
|
7312
|
-
}
|
|
7313
|
-
else if (ch.generator.playbackRate != undefined) {
|
|
7314
|
-
// If generator is an AudioBufferSourceNode
|
|
7315
|
-
const param = ch.generator.playbackRate;
|
|
7316
|
-
const bufferSamplesPerWave = isFilteredNoise ? 4 : 1024;
|
|
7317
|
-
param.linearRampToValueAtTime(endFreq / (context().sampleRate / bufferSamplesPerWave), ctx.currentTime + ((timeOff + duration) / 1000));
|
|
7318
|
-
}
|
|
7319
|
-
}
|
|
7320
|
-
ch.generator.connect(ch.gain);
|
|
7321
|
-
ch.gain.connect(destination);
|
|
7322
|
-
ch.generator.start();
|
|
7323
|
-
}
|
|
7324
|
-
idx += 12;
|
|
7325
|
-
ch.gain.gain.setValueAtTime(scaledStart, ctx.currentTime + (timeOff / 1000));
|
|
7326
|
-
timeOff += duration;
|
|
7327
|
-
// To prevent clipping, we ramp to this value slightly earlier than intended. This is so that we
|
|
7328
|
-
// can go for a smooth ramp to 0 in ch.mute() without this operation interrupting it. If we had
|
|
7329
|
-
// more accurate timing this would not be necessary, but we'd probably have to do something like
|
|
7330
|
-
// running a metronome in a webworker to get the level of precision we need
|
|
7331
|
-
const endTime = scaledEnd !== 0 && duration > 50 ? ((timeOff - 50) / 1000) : ((timeOff - 10) / 1000);
|
|
7332
|
-
ch.gain.gain.linearRampToValueAtTime(scaledEnd, ctx.currentTime + endTime);
|
|
7333
|
-
return loopAsync();
|
|
7334
|
-
};
|
|
7335
|
-
return loopAsync()
|
|
7336
|
-
.then(() => ch.remove());
|
|
7337
|
-
}
|
|
7338
|
-
AudioContextManager.playInstructionsAsync = playInstructionsAsync;
|
|
7339
7257
|
function tone(frequency, gain) {
|
|
7340
7258
|
if (frequency < 0)
|
|
7341
7259
|
return;
|
|
@@ -7474,6 +7392,126 @@ var pxsim;
|
|
|
7474
7392
|
function frequencyFromMidiNoteNumber(note) {
|
|
7475
7393
|
return 440 * Math.pow(2, (note - 69) / 12);
|
|
7476
7394
|
}
|
|
7395
|
+
function playInstructionsAsync(instructions, isCancelled, onPull) {
|
|
7396
|
+
return new Promise(async (resolve) => {
|
|
7397
|
+
let resolved = false;
|
|
7398
|
+
let ctx = context();
|
|
7399
|
+
let channel = new Channel();
|
|
7400
|
+
if (channels.length > 5)
|
|
7401
|
+
channels[0].remove();
|
|
7402
|
+
channels.push(channel);
|
|
7403
|
+
channel.gain = ctx.createGain();
|
|
7404
|
+
channel.gain.gain.value = 1;
|
|
7405
|
+
channel.gain.connect(destination);
|
|
7406
|
+
const oscillators = {};
|
|
7407
|
+
const gains = {};
|
|
7408
|
+
let startTime = ctx.currentTime;
|
|
7409
|
+
let currentTime = startTime;
|
|
7410
|
+
let currentWave = 0;
|
|
7411
|
+
let totalDuration = 0;
|
|
7412
|
+
/** Square waves are perceved as much louder than other sounds, so scale it down a bit to make it less jarring **/
|
|
7413
|
+
const scaleVol = (n, isSqWave) => (n / 1024) / 4 * (isSqWave ? .5 : 1);
|
|
7414
|
+
const disconnectNodes = () => {
|
|
7415
|
+
if (resolved)
|
|
7416
|
+
return;
|
|
7417
|
+
resolved = true;
|
|
7418
|
+
channel.disconnectNodes();
|
|
7419
|
+
for (const wave of Object.keys(oscillators)) {
|
|
7420
|
+
oscillators[wave].stop();
|
|
7421
|
+
oscillators[wave].disconnect();
|
|
7422
|
+
gains[wave].disconnect();
|
|
7423
|
+
}
|
|
7424
|
+
resolve();
|
|
7425
|
+
};
|
|
7426
|
+
for (let i = 0; i < instructions.length; i += 12) {
|
|
7427
|
+
const wave = instructions[i];
|
|
7428
|
+
const startFrequency = readUint16(instructions, i + 2);
|
|
7429
|
+
const duration = readUint16(instructions, i + 4) / 1000;
|
|
7430
|
+
const startVolume = readUint16(instructions, i + 6);
|
|
7431
|
+
const endVolume = readUint16(instructions, i + 8);
|
|
7432
|
+
const endFrequency = readUint16(instructions, i + 10);
|
|
7433
|
+
totalDuration += duration;
|
|
7434
|
+
const isSquareWave = 11 <= wave && wave <= 15;
|
|
7435
|
+
if (!oscillators[wave]) {
|
|
7436
|
+
oscillators[wave] = getGenerator(wave, startFrequency);
|
|
7437
|
+
gains[wave] = ctx.createGain();
|
|
7438
|
+
gains[wave].gain.value = 0;
|
|
7439
|
+
gains[wave].connect(channel.gain);
|
|
7440
|
+
oscillators[wave].connect(gains[wave]);
|
|
7441
|
+
oscillators[wave].start();
|
|
7442
|
+
}
|
|
7443
|
+
if (currentWave && wave !== currentWave) {
|
|
7444
|
+
gains[currentWave].gain.setTargetAtTime(0, currentTime, 0.015);
|
|
7445
|
+
}
|
|
7446
|
+
const osc = oscillators[wave];
|
|
7447
|
+
const gain = gains[wave];
|
|
7448
|
+
if (osc instanceof OscillatorNode) {
|
|
7449
|
+
osc.frequency.setValueAtTime(startFrequency, currentTime);
|
|
7450
|
+
osc.frequency.linearRampToValueAtTime(endFrequency, currentTime + duration);
|
|
7451
|
+
}
|
|
7452
|
+
else {
|
|
7453
|
+
const isFilteredNoise = wave == 4 || (16 <= wave && wave <= 18);
|
|
7454
|
+
if (isFilteredNoise)
|
|
7455
|
+
osc.playbackRate.linearRampToValueAtTime(endFrequency / (ctx.sampleRate / 4), currentTime + duration);
|
|
7456
|
+
else if (wave != 5)
|
|
7457
|
+
osc.playbackRate.linearRampToValueAtTime(endFrequency / (ctx.sampleRate / 1024), currentTime + duration);
|
|
7458
|
+
}
|
|
7459
|
+
gain.gain.setValueAtTime(scaleVol(startVolume, isSquareWave), currentTime);
|
|
7460
|
+
gain.gain.linearRampToValueAtTime(scaleVol(endVolume, isSquareWave), currentTime + duration);
|
|
7461
|
+
currentWave = wave;
|
|
7462
|
+
currentTime += duration;
|
|
7463
|
+
}
|
|
7464
|
+
channel.gain.gain.setTargetAtTime(0, currentTime, 0.015);
|
|
7465
|
+
if (isCancelled || onPull) {
|
|
7466
|
+
const handleAnimationFrame = () => {
|
|
7467
|
+
const time = ctx.currentTime;
|
|
7468
|
+
if (time > startTime + totalDuration) {
|
|
7469
|
+
return;
|
|
7470
|
+
}
|
|
7471
|
+
if (isCancelled && isCancelled()) {
|
|
7472
|
+
disconnectNodes();
|
|
7473
|
+
return;
|
|
7474
|
+
}
|
|
7475
|
+
const { frequency, volume } = findFrequencyAndVolumeAtTime((time - startTime) * 1000, instructions);
|
|
7476
|
+
onPull(frequency, volume / 1024);
|
|
7477
|
+
requestAnimationFrame(handleAnimationFrame);
|
|
7478
|
+
};
|
|
7479
|
+
requestAnimationFrame(handleAnimationFrame);
|
|
7480
|
+
}
|
|
7481
|
+
await pxsim.U.delay(totalDuration * 1000);
|
|
7482
|
+
disconnectNodes();
|
|
7483
|
+
});
|
|
7484
|
+
}
|
|
7485
|
+
AudioContextManager.playInstructionsAsync = playInstructionsAsync;
|
|
7486
|
+
function readUint16(buf, offset) {
|
|
7487
|
+
const temp = new Uint8Array(2);
|
|
7488
|
+
temp[0] = buf[offset];
|
|
7489
|
+
temp[1] = buf[offset + 1];
|
|
7490
|
+
return new Uint16Array(temp.buffer)[0];
|
|
7491
|
+
}
|
|
7492
|
+
function findFrequencyAndVolumeAtTime(millis, instructions) {
|
|
7493
|
+
let currentTime = 0;
|
|
7494
|
+
for (let i = 0; i < instructions.length; i += 12) {
|
|
7495
|
+
const startFrequency = readUint16(instructions, i + 2);
|
|
7496
|
+
const duration = readUint16(instructions, i + 4);
|
|
7497
|
+
const startVolume = readUint16(instructions, i + 6);
|
|
7498
|
+
const endVolume = readUint16(instructions, i + 8);
|
|
7499
|
+
const endFrequency = readUint16(instructions, i + 10);
|
|
7500
|
+
if (currentTime + duration < millis) {
|
|
7501
|
+
currentTime += duration;
|
|
7502
|
+
continue;
|
|
7503
|
+
}
|
|
7504
|
+
const offset = (millis - currentTime) / duration;
|
|
7505
|
+
return {
|
|
7506
|
+
frequency: startFrequency + (endFrequency - startFrequency) * offset,
|
|
7507
|
+
volume: startVolume + (endVolume - startVolume) * offset,
|
|
7508
|
+
};
|
|
7509
|
+
}
|
|
7510
|
+
return {
|
|
7511
|
+
frequency: -1,
|
|
7512
|
+
volume: -1
|
|
7513
|
+
};
|
|
7514
|
+
}
|
|
7477
7515
|
function sendMidiMessage(buf) {
|
|
7478
7516
|
const data = buf.data;
|
|
7479
7517
|
if (!data.length) // garbage.
|