adding sound emulation in browser
This commit is contained in:
parent
a751be2e97
commit
d323995196
@ -331,6 +331,58 @@ namespace ks.rt.micro_bit {
|
|||||||
|
|
||||||
export function servoSetPulse(pin: Pin, micros:number) {
|
export function servoSetPulse(pin: Pin, micros:number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module AudioContextManager {
|
||||||
|
var _context : any; // AudioContext
|
||||||
|
var _vco : any; //OscillatorNode;
|
||||||
|
var _vca: any; // GainNode;
|
||||||
|
|
||||||
|
function context() : any {
|
||||||
|
if (!_context) _context = freshContext();
|
||||||
|
return _context;
|
||||||
|
}
|
||||||
|
|
||||||
|
function freshContext() : any {
|
||||||
|
(<any>window).AudioContext = (<any>window).AudioContext || (<any>window).webkitAudioContext;
|
||||||
|
if ((<any>window).AudioContext) {
|
||||||
|
try {
|
||||||
|
// this call my crash.
|
||||||
|
// SyntaxError: audio resources unavailable for AudioContext construction
|
||||||
|
return new (<any>window).AudioContext();
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stop() {
|
||||||
|
if (_vca) _vca.gain.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tone(frequency: number, gain: number) {
|
||||||
|
if (frequency <= 0) return;
|
||||||
|
var ctx = context();
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
gain = Math.max(0, Math.min(1, gain));
|
||||||
|
if (!_vco) {
|
||||||
|
try {
|
||||||
|
_vco = ctx.createOscillator();
|
||||||
|
_vca = ctx.createGain();
|
||||||
|
_vco.connect(_vca);
|
||||||
|
_vca.connect(ctx.destination);
|
||||||
|
_vca.gain.value = gain;
|
||||||
|
_vco.start(0);
|
||||||
|
} catch(e) {
|
||||||
|
_vco = undefined;
|
||||||
|
_vca = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_vco.frequency.value = frequency;
|
||||||
|
_vca.gain.value = gain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function enablePitch(pin: Pin) {
|
export function enablePitch(pin: Pin) {
|
||||||
pin.mode = PinMode.Analog | PinMode.Output;
|
pin.mode = PinMode.Analog | PinMode.Output;
|
||||||
@ -338,7 +390,11 @@ namespace ks.rt.micro_bit {
|
|||||||
|
|
||||||
export function pitch(frequency: number, ms: number) {
|
export function pitch(frequency: number, ms: number) {
|
||||||
let cb = getResume();
|
let cb = getResume();
|
||||||
setTimeout(() => { cb() }, ms);
|
AudioContextManager.tone(frequency, 1);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (ms > 0) AudioContextManager.stop();
|
||||||
|
cb()
|
||||||
|
}, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
184
sim/state.ts
184
sim/state.ts
@ -2,141 +2,141 @@ namespace ks.rt.micro_bit {
|
|||||||
export interface RuntimeOptions {
|
export interface RuntimeOptions {
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DisplayMode {
|
export enum DisplayMode {
|
||||||
bw,
|
bw,
|
||||||
greyscale
|
greyscale
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PinMode {
|
export enum PinMode {
|
||||||
Unused = 0,
|
Unused = 0,
|
||||||
Digital = 0x0001,
|
Digital = 0x0001,
|
||||||
Analog = 0x0002,
|
Analog = 0x0002,
|
||||||
Input = 0x0004,
|
Input = 0x0004,
|
||||||
Output = 0x0008,
|
Output = 0x0008,
|
||||||
Touch = 0x0010
|
Touch = 0x0010
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Pin {
|
export class Pin {
|
||||||
constructor(public id: number) {}
|
constructor(public id: number) { }
|
||||||
touched = false;
|
touched = false;
|
||||||
value = 0;
|
value = 0;
|
||||||
mode = PinMode.Unused;
|
mode = PinMode.Unused;
|
||||||
|
|
||||||
isTouched() : boolean {
|
isTouched(): boolean {
|
||||||
this.mode = PinMode.Touch;
|
this.mode = PinMode.Touch;
|
||||||
return this.touched;
|
return this.touched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Button {
|
export class Button {
|
||||||
constructor(public id : number) {}
|
constructor(public id: number) { }
|
||||||
pressed: boolean;
|
pressed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EventBus {
|
export class EventBus {
|
||||||
private queues : Map<EventQueue<number>> = {};
|
private queues: Map<EventQueue<number>> = {};
|
||||||
|
|
||||||
constructor(private runtime : Runtime) { }
|
constructor(private runtime: Runtime) { }
|
||||||
|
|
||||||
listen(id:number, evid:number, handler: RefAction) {
|
listen(id: number, evid: number, handler: RefAction) {
|
||||||
let k = id + ':' + evid;
|
let k = id + ':' + evid;
|
||||||
let queue = this.queues[k];
|
let queue = this.queues[k];
|
||||||
if (!queue) queue = this.queues[k] = new EventQueue<number>(this.runtime);
|
if (!queue) queue = this.queues[k] = new EventQueue<number>(this.runtime);
|
||||||
queue.handler = handler;
|
queue.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue(id: number, evid: number, value: number = 0) {
|
queue(id: number, evid: number, value: number = 0) {
|
||||||
let k = id + ':' + evid;
|
let k = id + ':' + evid;
|
||||||
let queue = this.queues[k];
|
let queue = this.queues[k];
|
||||||
if (queue) queue.push(value);
|
if (queue) queue.push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PacketBuffer {
|
export interface PacketBuffer {
|
||||||
data:number[];
|
data: number[];
|
||||||
rssi?:number;
|
rssi?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RadioDatagram {
|
export class RadioDatagram {
|
||||||
datagram: PacketBuffer[] = [];
|
datagram: PacketBuffer[] = [];
|
||||||
lastReceived: PacketBuffer = {
|
lastReceived: PacketBuffer = {
|
||||||
data:[0,0,0,0],
|
data: [0, 0, 0, 0],
|
||||||
rssi: -1
|
rssi: -1
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(private runtime : Runtime) {
|
constructor(private runtime: Runtime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
queue(packet : PacketBuffer) {
|
queue(packet: PacketBuffer) {
|
||||||
if (this.datagram.length < 5) {
|
if (this.datagram.length < 5) {
|
||||||
this.datagram.push(packet);
|
this.datagram.push(packet);
|
||||||
let ens = enums();
|
let ens = enums();
|
||||||
(<Board>runtime.board).bus.queue(ens.MICROBIT_ID_RADIO, ens.MICROBIT_RADIO_EVT_DATAGRAM);
|
(<Board>runtime.board).bus.queue(ens.MICROBIT_ID_RADIO, ens.MICROBIT_RADIO_EVT_DATAGRAM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send(buffer : number[]) {
|
send(buffer: number[]) {
|
||||||
Runtime.postMessage(<SimulatorRadioPacketMessage>{
|
Runtime.postMessage(<SimulatorRadioPacketMessage>{
|
||||||
type:'radiopacket',
|
type: 'radiopacket',
|
||||||
data: buffer.slice(0, 8)
|
data: buffer.slice(0, 8)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
recv() : PacketBuffer {
|
recv(): PacketBuffer {
|
||||||
var r = this.datagram.shift();
|
var r = this.datagram.shift();
|
||||||
if (!r) r = {
|
if (!r) r = {
|
||||||
data:[0,0,0,0],
|
data: [0, 0, 0, 0],
|
||||||
rssi: -1
|
rssi: -1
|
||||||
};
|
};
|
||||||
return this.lastReceived = r;
|
return this.lastReceived = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RadioBus {
|
export class RadioBus {
|
||||||
// uint8_t radioDefaultGroup = MICROBIT_RADIO_DEFAULT_GROUP;
|
// uint8_t radioDefaultGroup = MICROBIT_RADIO_DEFAULT_GROUP;
|
||||||
groupId = 0; // todo
|
groupId = 0; // todo
|
||||||
datagram : RadioDatagram;
|
datagram: RadioDatagram;
|
||||||
|
|
||||||
constructor(private runtime : Runtime) {
|
constructor(private runtime: Runtime) {
|
||||||
this.datagram = new RadioDatagram(runtime);
|
this.datagram = new RadioDatagram(runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
setGroup(id: number) {
|
setGroup(id: number) {
|
||||||
this.groupId = id & 0xff; // byte only
|
this.groupId = id & 0xff; // byte only
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(msg: number) {
|
broadcast(msg: number) {
|
||||||
let ens = enums();
|
let ens = enums();
|
||||||
Runtime.postMessage(<SimulatorEventBusMessage>{
|
Runtime.postMessage(<SimulatorEventBusMessage>{
|
||||||
type:'eventbus',
|
type: 'eventbus',
|
||||||
id: ens.MES_BROADCAST_GENERAL_ID,
|
id: ens.MES_BROADCAST_GENERAL_ID,
|
||||||
eventid: msg
|
eventid: msg
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SimulatorEventBusMessage extends SimulatorMessage {
|
export interface SimulatorEventBusMessage extends SimulatorMessage {
|
||||||
id: number;
|
id: number;
|
||||||
eventid: number;
|
eventid: number;
|
||||||
value?: number;
|
value?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SimulatorSerialMessage extends SimulatorMessage {
|
export interface SimulatorSerialMessage extends SimulatorMessage {
|
||||||
id:string;
|
id: string;
|
||||||
data: string;
|
data: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SimulatorRadioPacketMessage extends SimulatorMessage {
|
export interface SimulatorRadioPacketMessage extends SimulatorMessage {
|
||||||
data: number[];
|
data: number[];
|
||||||
rssi?: number;
|
rssi?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Board extends BaseBoard {
|
export class Board extends BaseBoard {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
// the bus
|
// the bus
|
||||||
bus : EventBus;
|
bus: EventBus;
|
||||||
radio: RadioBus;
|
radio: RadioBus;
|
||||||
|
|
||||||
// display
|
// display
|
||||||
@ -147,28 +147,28 @@ namespace ks.rt.micro_bit {
|
|||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
usesButtonAB: boolean = false;
|
usesButtonAB: boolean = false;
|
||||||
buttons : Button[];
|
buttons: Button[];
|
||||||
|
|
||||||
// pins
|
// pins
|
||||||
pins : Pin[];
|
pins: Pin[];
|
||||||
|
|
||||||
// serial
|
// serial
|
||||||
serialIn: string[] = [];
|
serialIn: string[] = [];
|
||||||
|
|
||||||
// sensors
|
// sensors
|
||||||
usesAcceleration = false;
|
usesAcceleration = false;
|
||||||
acceleration = [0, 0, -1023];
|
acceleration = [0, 0, -1023];
|
||||||
|
|
||||||
usesHeading = false;
|
usesHeading = false;
|
||||||
heading = 90;
|
heading = 90;
|
||||||
|
|
||||||
temperature = 21;
|
temperature = 21;
|
||||||
|
|
||||||
usesLightLevel = false;
|
usesLightLevel = false;
|
||||||
lightLevel = 128;
|
lightLevel = 128;
|
||||||
|
|
||||||
animationQ: AnimationQueue;
|
animationQ: AnimationQueue;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.id = "b" + Math.random();
|
this.id = "b" + Math.random();
|
||||||
@ -199,25 +199,25 @@ namespace ks.rt.micro_bit {
|
|||||||
new Pin(ens.MICROBIT_ID_IO_P14),
|
new Pin(ens.MICROBIT_ID_IO_P14),
|
||||||
new Pin(ens.MICROBIT_ID_IO_P15),
|
new Pin(ens.MICROBIT_ID_IO_P15),
|
||||||
new Pin(ens.MICROBIT_ID_IO_P16),
|
new Pin(ens.MICROBIT_ID_IO_P16),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
new Pin(ens.MICROBIT_ID_IO_P19),
|
new Pin(ens.MICROBIT_ID_IO_P19),
|
||||||
new Pin(ens.MICROBIT_ID_IO_P20)
|
new Pin(ens.MICROBIT_ID_IO_P20)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
initAsync(msg : SimulatorRunMessage) : Promise<void> {
|
initAsync(msg: SimulatorRunMessage): Promise<void> {
|
||||||
let options = (msg.options || {}) as RuntimeOptions;
|
let options = (msg.options || {}) as RuntimeOptions;
|
||||||
let theme : micro_bit.IBoardTheme;
|
let theme: micro_bit.IBoardTheme;
|
||||||
switch(options.theme) {
|
switch (options.theme) {
|
||||||
case 'blue': theme = micro_bit.themes[0]; break;
|
case 'blue': theme = micro_bit.themes[0]; break;
|
||||||
case 'yellow': theme = micro_bit.themes[1]; break;
|
case 'yellow': theme = micro_bit.themes[1]; break;
|
||||||
case 'green': theme = micro_bit.themes[2]; break;
|
case 'green': theme = micro_bit.themes[2]; break;
|
||||||
case 'red': theme = micro_bit.themes[3]; break;
|
case 'red': theme = micro_bit.themes[3]; break;
|
||||||
default: theme = ks.rt.micro_bit.randomTheme();
|
default: theme = ks.rt.micro_bit.randomTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('setting up microbit simulator')
|
console.log('setting up microbit simulator')
|
||||||
let view = new ks.rt.micro_bit.MicrobitBoardSvg({
|
let view = new ks.rt.micro_bit.MicrobitBoardSvg({
|
||||||
theme: theme,
|
theme: theme,
|
||||||
@ -225,56 +225,56 @@ namespace ks.rt.micro_bit {
|
|||||||
})
|
})
|
||||||
document.body.innerHTML = ''; // clear children
|
document.body.innerHTML = ''; // clear children
|
||||||
document.body.appendChild(view.element);
|
document.body.appendChild(view.element);
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveMessage(msg: SimulatorMessage) {
|
receiveMessage(msg: SimulatorMessage) {
|
||||||
if (!runtime || runtime.dead) return;
|
if (!runtime || runtime.dead) return;
|
||||||
|
|
||||||
switch(msg.type || "") {
|
switch (msg.type || "") {
|
||||||
case 'eventbus':
|
case 'eventbus':
|
||||||
let ev = <SimulatorEventBusMessage>msg;
|
let ev = <SimulatorEventBusMessage>msg;
|
||||||
this.bus.queue(ev.id, ev.eventid, ev.value);
|
this.bus.queue(ev.id, ev.eventid, ev.value);
|
||||||
break;
|
break;
|
||||||
case 'serial':
|
case 'serial':
|
||||||
this.serialIn.push((<SimulatorSerialMessage>msg).data || '');
|
this.serialIn.push((<SimulatorSerialMessage>msg).data || '');
|
||||||
break;
|
break;
|
||||||
case 'radiopacket':
|
case 'radiopacket':
|
||||||
let packet = <SimulatorRadioPacketMessage>msg;
|
let packet = <SimulatorRadioPacketMessage>msg;
|
||||||
this.radio.datagram.queue({ data: packet.data || [], rssi: packet.rssi || 0})
|
this.radio.datagram.queue({ data: packet.data || [], rssi: packet.rssi || 0 })
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readSerial() {
|
readSerial() {
|
||||||
let v = this.serialIn.shift() || '';
|
let v = this.serialIn.shift() || '';
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialOutBuffer: string = '';
|
serialOutBuffer: string = '';
|
||||||
writeSerial(s : string) {
|
writeSerial(s: string) {
|
||||||
for(let i = 0; i < s.length;++i) {
|
for (let i = 0; i < s.length; ++i) {
|
||||||
let c = s[i];
|
let c = s[i];
|
||||||
switch(c) {
|
switch (c) {
|
||||||
case '\n':
|
case '\n':
|
||||||
Runtime.postMessage(<SimulatorSerialMessage>{
|
Runtime.postMessage(<SimulatorSerialMessage>{
|
||||||
type: 'serial',
|
type: 'serial',
|
||||||
data: this.serialOutBuffer,
|
data: this.serialOutBuffer,
|
||||||
id: runtime.id
|
id: runtime.id
|
||||||
})
|
})
|
||||||
this.serialOutBuffer = ''
|
this.serialOutBuffer = ''
|
||||||
break;
|
break;
|
||||||
case '\r': continue;
|
case '\r': continue;
|
||||||
default: this.serialOutBuffer += c;
|
default: this.serialOutBuffer += c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Image {
|
export class Image {
|
||||||
public width: number;
|
public width: number;
|
||||||
public data: number[];
|
public data: number[];
|
||||||
constructor(width: number, data: number[]) {
|
constructor(width: number, data: number[]) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@ -295,8 +295,8 @@ namespace ks.rt.micro_bit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public clear() : void {
|
public clear(): void {
|
||||||
for(var i = 0;i < this.data.length; ++i)
|
for (var i = 0; i < this.data.length; ++i)
|
||||||
this.data[i] = 0;
|
this.data[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user