Merge pull request #1998 from bromagosa/sound-recorder

Added sound recorder widget
upd4.2
Jens Mönig 2018-01-25 08:42:13 +01:00 zatwierdzone przez GitHub
commit c346ef43d7
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
1 zmienionych plików z 277 dodań i 9 usunięć

286
gui.js
Wyświetl plik

@ -2296,6 +2296,22 @@ IDE_Morph.prototype.newCamSprite = function () {
camDialog.popUp(this.world());
};
IDE_Morph.prototype.recordNewSound = function () {
var soundRecorder,
myself = this;
soundRecorder = new SoundRecorderDialogMorph(
function (sound) {
if (sound) {
myself.currentSprite.addSound(sound, myself.newSoundName('recording'));
myself.spriteBar.tabBar.tabTo('sounds');
myself.hasChangedMedia = true;
}
});
soundRecorder.popUp(this.world());
};
IDE_Morph.prototype.duplicateSprite = function (sprite) {
var duplicate = sprite.fullCopy();
duplicate.setPosition(this.world().hand.position());
@ -2346,17 +2362,37 @@ IDE_Morph.prototype.removeSprite = function (sprite) {
this.selectSprite(this.currentSprite);
};
IDE_Morph.prototype.newSoundName = function (name) {
var lastSound =
this.currentSprite.sounds.at(
this.currentSprite.sounds.length());
return this.newName(
lastSound.name || name,
this.currentSprite.sounds.asArray().map(
function (eachSound) {
return eachSound.name;
}
)
);
};
IDE_Morph.prototype.newSpriteName = function (name, ignoredSprite) {
var ix = name.indexOf('('),
stem = (ix < 0) ? name : name.substring(0, ix),
count = 1,
newName = stem,
all = this.sprites.asArray().concat(this.stage).filter(
var all = this.sprites.asArray().concat(this.stage).filter(
function (each) {return each !== ignoredSprite; }
).map(
function (each) {return each.name; }
);
while (contains(all, newName)) {
return this.newName(name, all)
};
IDE_Morph.prototype.newName = function (name, elements) {
var ix = name.indexOf('('),
stem = (ix < 0) ? name : name.substring(0, ix),
count = 1,
newName = stem;
while (contains(elements, newName)) {
count += 1;
newName = stem + '(' + count + ')';
}
@ -8114,7 +8150,7 @@ SoundIconMorph.prototype.renameSound = function () {
SoundIconMorph.prototype.removeSound = function () {
var jukebox = this.parentThatIsA(JukeboxMorph),
idx = this.parent.children.indexOf(this);
idx = this.parent.children.indexOf(this + 1);
jukebox.removeSound(idx);
};
@ -8183,7 +8219,9 @@ JukeboxMorph.prototype.updateList = function () {
oldFlag = Morph.prototype.trackChanges,
icon,
template,
txt;
txt,
ide = this.sprite.parentThatIsA(IDE_Morph),
recordButton;
this.changed();
oldFlag = Morph.prototype.trackChanges;
@ -8204,7 +8242,31 @@ JukeboxMorph.prototype.updateList = function () {
txt.setColor(SpriteMorph.prototype.paletteTextColor);
txt.setPosition(new Point(x, y));
this.addContents(txt);
y = txt.bottom() + padding;
recordButton = new PushButtonMorph(
ide,
'recordNewSound',
new SymbolMorph('circleSolid', 15),
);
recordButton.padding = 0;
recordButton.corner = 12;
recordButton.color = IDE_Morph.prototype.groupColor;
recordButton.highlightColor = IDE_Morph.prototype.frameColor.darker(50);
recordButton.pressColor = recordButton.highlightColor;
recordButton.labelMinExtent = new Point(36, 18);
recordButton.labelShadowOffset = new Point(-1, -1);
recordButton.labelShadowColor = recordButton.highlightColor;
recordButton.labelColor = TurtleIconMorph.prototype.labelColor;
recordButton.contrast = this.buttonContrast;
recordButton.drawNew();
recordButton.hint = 'Record a new sound';
recordButton.fixLayout();
recordButton.label.setColor(new Color(255, 20, 20));
recordButton.setPosition(txt.bottomLeft().add(new Point(0, padding * 2)));
this.addContents(recordButton);
y = recordButton.bottom() + padding;
this.sprite.sounds.asArray().forEach(function (sound) {
template = icon = new SoundIconMorph(sound, template);
@ -8674,3 +8736,209 @@ CamSnapshotDialogMorph.prototype.close = function () {
}
CamSnapshotDialogMorph.uber.destroy.call(this);
};
// SoundRecorderDialogMorph ////////////////////////////////////////////////////
/*
I am a dialog morph that lets users record sound snippets for their
sprites or Stage.
*/
// SoundRecorderDialogMorph inherits from DialogBoxMorph:
SoundRecorderDialogMorph.prototype = new DialogBoxMorph();
SoundRecorderDialogMorph.prototype.constructor = SoundRecorderDialogMorph;
SoundRecorderDialogMorph.uber = DialogBoxMorph.prototype;
// SoundRecorderDialogMorph instance creation
function SoundRecorderDialogMorph(onAccept) {
this.init(onAccept);
}
SoundRecorderDialogMorph.prototype.init = function (onAccept) {
var myself = this;
this.padding = 10;
this.accept = onAccept;
this.mediaRecorder = null; // an HTML5 MediaRecorder object
this.audioElement = document.createElement('audio');
this.audioElement.hidden = true;
this.audioElement.onended = function (event) {
myself.stop();
};
document.body.appendChild(this.audioElement);
this.recordButton = null;
this.stopButton = null;
this.playButton = null;
this.progressBar = new BoxMorph();
SoundRecorderDialogMorph.uber.init.call(this);
this.labelString = 'Sound Recorder';
this.createLabel();
this.buildContents();
};
SoundRecorderDialogMorph.prototype.buildContents = function () {
var myself = this,
audioChunks = [];
this.recordButton = new PushButtonMorph(
this,
'record',
new SymbolMorph('circleSolid', 10)
);
this.recordButton.drawNew();
this.recordButton.fixLayout();
this.stopButton = new PushButtonMorph(
this,
'stop',
new SymbolMorph('rectangleSolid', 10)
);
this.stopButton.drawNew();
this.stopButton.fixLayout();
this.playButton = new PushButtonMorph(
this,
'play',
new SymbolMorph('pointRight', 10)
);
this.playButton.drawNew();
this.playButton.fixLayout();
this.buildProgressBar();
this.addBody(new AlignmentMorph('row', this.padding));
this.body.add(this.recordButton);
this.body.add(this.stopButton);
this.body.add(this.playButton);
this.body.add(this.progressBar);
this.body.fixLayout();
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(function (stream) {
myself.mediaRecorder = new MediaRecorder(stream);
myself.mediaRecorder.ondataavailable = function (event) {
audioChunks.push(event.data);
};
myself.mediaRecorder.onstop = function (event) {
myself.audioElement.src =
window.URL.createObjectURL(
new Blob(
audioChunks,
{'type': 'audio/ogg; codecs=opus'}
)
);
myself.audioElement.load();
audioChunks = [];
};
});
}
this.addButton('ok', 'Save');
this.addButton('cancel', 'Cancel');
this.fixLayout();
this.drawNew();
};
SoundRecorderDialogMorph.prototype.buildProgressBar = function () {
var line = new Morph(),
myself = this;
this.progressBar.setExtent(new Point(150, 20));
this.progressBar.setColor(new Color(200, 200, 200));
this.progressBar.setBorderWidth(1);
this.progressBar.setBorderColor(new Color(150, 150, 150));
line.setExtent(new Point(130, 2));
line.setColor(new Color(50, 50, 50));
line.setCenter(this.progressBar.center());
this.progressBar.add(line);
this.progressBar.indicator = new Morph();
this.progressBar.indicator.setExtent(new Point(5, 15));
this.progressBar.indicator.setColor(new Color(50, 200, 50));
this.progressBar.indicator.setCenter(line.leftCenter());
this.progressBar.add(this.progressBar.indicator);
this.progressBar.setPercentage = function (percentage) {
this.indicator.setLeft(
line.left() +
(line.width() / 100 * percentage) -
this.indicator.width() / 2)
};
this.progressBar.step = function () {
if (myself.audioElement.duration) {
this.setPercentage(
myself.audioElement.currentTime /
myself.audioElement.duration * 100);
} else {
this.setPercentage(0);
}
};
};
SoundRecorderDialogMorph.prototype.record = function () {
this.mediaRecorder.start();
this.recordButton.label.setColor(new Color(255, 0, 0));
this.playButton.label.setColor(new Color(0, 0, 0));
};
SoundRecorderDialogMorph.prototype.stop = function () {
var myself = this;
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
this.mediaRecorder.stop();
}
this.audioElement.pause();
this.audioElement.currentTime = 0;
this.recordButton.label.setColor(new Color(0, 0, 0));
this.playButton.label.setColor(new Color(0, 0, 0));
};
SoundRecorderDialogMorph.prototype.play = function () {
this.stop();
this.audioElement.oncanplaythrough = function () {
this.play();
this.oncanplaythrough = nop;
}
this.playButton.label.setColor(new Color(0, 255, 0));
};
SoundRecorderDialogMorph.prototype.ok = function () {
var myself = this;
this.stop();
this.audioElement.oncanplaythrough = function () {
if (this.duration && this.duration !== Infinity) {
myself.accept(this);
this.oncanplaythrough = nop;
myself.destroy();
} else {
// For some reason, we need to play the sound
// at least once to get its duration.
myself.buttons.children.forEach(function (button) {
button.disable();
});
this.play();
}
};
};
SoundRecorderDialogMorph.prototype.destroy = function () {
this.stop();
this.audioElement.remove();
if (this.mediaRecorder) {
this.mediaRecorder.stream.getTracks()[0].stop();
}
SoundRecorderDialogMorph.uber.destroy.call(this);
};