Skip to content
This repository has been archived by the owner on Feb 7, 2019. It is now read-only.

Add the Sound library #53

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions build/strings/en/documentation.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,25 @@
"Shapes_HideShape_ShapeName": "The name of the shape.",
"Shapes_ShowShape": "Shows a previously hidden shape.",
"Shapes_ShowShape_ShapeName": "The name of the shape.",
"Sound": "The Sound object provides operations that allow the playback of sounds. Some sample sounds are provided along with the library.",
"Sound_PlayClick" : "Plays the Click Sound.",
"Sound_PlayClickAndWait" : "Plays the Click Sound and waits for it to finish.",
"Sound_PlayChime" : "Plays the Chime Sound. ",
"Sound_PlayChimeAndWait" : "Plays the Chime Sound and waits for it to finish.",
"Sound_PlayChimes" : "Plays the Chimes Sound.",
"Sound_PlayChimesAndWait" : "Plays the Chimes Sound and waits for it to finish.",
"Sound_PlayBellRing" : "Plays the Bell Ring Sound.",
"Sound_PlayBellRingAndWait" : "Plays the Bell Ring Sound and waits for it to finish.",
"Sound_PlayMusic" : "Plays musical notes.",
"Sound_PlayMusic_Notes" : "A set of musical notes to play. The format is a subset of the Music Macro Language supported by QBasic.",
"Sound_Play" : "Plays an audio file. This could be an mp3 or wav or wma file. Other file formats may or may not play depending on the audio codecs installed on the user's computer. If the file was already paused, this operation will resume from the position where the playback was paused.",
"Sound_Play_FilePath" : "The path for the audio file. This could either be a local file (e.g.: c:\\music\\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).",
"Sound_PlayAndWait" : "Plays an audio file and waits until it is finished playing. This could be an mp3, wav, or wma file. Other file formats may or may not play depending on the audio codecs installed on the user's computer. If the file was already paused, this operation will resume from the position where the playback was paused.",
"Sound_PlayAndWait_FilePath" : "The path for the audio file. This could either be a local file (e.g.: c:\\music\\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).",
"Sound_Pause" : "Pauses playback of an audio file. If the file was not already playing, this operation will not do anything.",
"Sound_Pause_FilePath" : "The path for the audio file. This could either be a local file (e.g.: c:\\music\\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).",
"Sound_Stop" : "Stops playback of an audio file. If the file was not already playing, this operation will not do anything.",
"Sound_Stop_FilePath" : "The path for the audio file. This could either be a local file (e.g.: c:\\music\\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).",
"Stack": "This object provides a way of storing values just like stacking up a plate. You can push a value to the top of the stack and pop it off. You can only pop the values one by one off the stack and the last pushed value will be the first one to pop out.",
"Stack_PushValue": "Pushes a value to the specified stack.",
"Stack_PushValue_StackName": "The name of the stack.",
Expand Down
2 changes: 1 addition & 1 deletion build/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function factory(params: IFactoryParams): webpack.Configuration {
]
},
{
test: /\.(png|gif)$/,
test: /\.(png|gif|wav)$/,
use: [
{
loader: "file-loader",
Expand Down
12 changes: 12 additions & 0 deletions src/app/components/common/sound-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ISoundLibraryPlugin } from "../../../compiler/runtime/libraries/sound";

export class SoundLibraryPlugin implements ISoundLibraryPlugin {
Copy link
Member

@OmarTawfik OmarTawfik Nov 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you document where did we get the sound files from?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sound files come from the Desktop copy of the code -- do you know where that got it from? I don't recall seeing any comments.


public playAudio(audioFile: string): void {
if (audioFile !== "")
{
let audio = new Audio(audioFile);
audio.play();
}
}
}
Binary file added src/app/content/sounds/BellRing.wav
Binary file not shown.
Binary file added src/app/content/sounds/Chime.wav
Binary file not shown.
Binary file added src/app/content/sounds/Click.wav
Binary file not shown.
Binary file added src/app/content/sounds/Pause.wav
Binary file not shown.
23 changes: 23 additions & 0 deletions src/compiler/runtime/libraries-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,29 @@ export class LibrariesMetadata {
// No Events
});

public readonly Sound: TypeMetadata = new TypeMetadata("Sound",
{
PlayClick: new MethodMetadata("Sound", "PlayClick", false, []),
PlayClickAndWait: new MethodMetadata("Sound", "PlayClickAndWait", false, []),
PlayChime: new MethodMetadata("Sound", "PlayChime", false, []),
PlayChimeAndWait: new MethodMetadata("Sound", "PlayChimeAndWait", false, []),
PlayChimes: new MethodMetadata("Sound", "PlayChimes", false, []),
PlayChimesAndWait: new MethodMetadata("Sound", "PlayChimesAndWait", false, []),
PlayBellRing: new MethodMetadata("Sound", "PlayBellRing", false, []),
PlayBellRingAndWait: new MethodMetadata("Sound", "PlayBellRingAndWait", false, []),
PlayMusic: new MethodMetadata("Sound", "PlayMusic", false, ["Notes"]),
Play: new MethodMetadata("Sound", "Play", false, ["FilePath"]),
PlayAndWait: new MethodMetadata("Sound", "PlayAndWait", false, ["FilePath"]),
Pause: new MethodMetadata("Sound", "Pause", false, ["FilePath"]),
Stop: new MethodMetadata("Sound", "Stop", false, ["FilePath"])
},
{
// No Properties
},
{
// No Events
});

public readonly Stack: TypeMetadata = new TypeMetadata("Stack",
{
PushValue: new MethodMetadata("Stack", "PushValue", false, ["StackName", "Value"]),
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/runtime/libraries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TextWindowLibrary } from "./libraries/text-window";
import { ProgramLibrary } from "./libraries/program";
import { ClockLibrary } from "./libraries/clock";
import { ArrayLibrary } from "./libraries/array";
import { SoundLibrary } from "./libraries/sound";
import { StackLibrary } from "./libraries/stack";
import { BaseValue } from "./values/base-value";
import { LibrariesMetadata } from "./libraries-metadata";
Expand Down Expand Up @@ -41,6 +42,7 @@ export class RuntimeLibraries {
public readonly Math: MathLibrary = new MathLibrary();
public readonly Program: ProgramLibrary = new ProgramLibrary();
public readonly Shapes: ShapesLibrary = new ShapesLibrary();
public readonly Sound: SoundLibrary = new SoundLibrary();
public readonly Stack: StackLibrary = new StackLibrary();
public readonly TextWindow: TextWindowLibrary = new TextWindowLibrary();
// TODO: public readonly Turtle: TurtleLibrary = new TurtleLibrary();
Expand Down
74 changes: 74 additions & 0 deletions src/compiler/runtime/libraries/sound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { LibraryTypeInstance, LibraryMethodInstance, LibraryPropertyInstance, LibraryEventInstance } from "../libraries";
import { SoundLibraryPlugin } from "../../../app/components/common/sound-plugin";

export const ClickSound = require("../../../app/content/sounds/click.wav");
export const ChimeSound = require("../../../app/content/sounds/chime.wav");
export const ChimesSound = require("../../../app/content/sounds/pause.wav");
export const BellRingSound = require("../../../app/content/sounds/bellring.wav");

enum Sound {
Click,
Chime,
Chimes,
BellRing
}

export interface ISoundLibraryPlugin {
playAudio(audioFile : string): void;
}

export class SoundLibrary implements LibraryTypeInstance {
private _pluginInstance: ISoundLibraryPlugin | undefined;

public get plugin(): ISoundLibraryPlugin {
if (!this._pluginInstance) {
this._pluginInstance = new SoundLibraryPlugin();
}

return this._pluginInstance;
}

public set plugin(plugin: ISoundLibraryPlugin) {
this._pluginInstance = plugin;
}

private executePlayStockSound(soundName: Sound): void {
let audioFile : string = "";
switch (soundName) {
case Sound.Click:
audioFile = ClickSound;
break;
case Sound.Chime:
audioFile = ChimeSound;
break;
case Sound.Chimes:
audioFile = ChimesSound;
break;
case Sound.BellRing:
audioFile = BellRingSound;
break;
}

this.plugin.playAudio(audioFile);
}

public readonly methods: { readonly [name: string]: LibraryMethodInstance } = {
PlayClick: { execute: this.executePlayStockSound.bind(this, Sound.Click) },
PlayClickAndWait: { execute: () => { throw new Error("Not Implemented yet."); } },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest not adding the NYI methods until they're actually functional. No need to show it in the UI and crash afterwards if they're not implemented yet.

PlayChime: { execute: this.executePlayStockSound.bind(this, Sound.Chime) },
PlayChimeAndWait: { execute: () => { throw new Error("Not Implemented yet."); } },
PlayChimes: { execute: this.executePlayStockSound.bind(this, Sound.Chimes) },
PlayChimesAndWait: { execute: () => { throw new Error("Not Implemented yet."); } },
PlayBellRing: { execute: this.executePlayStockSound.bind(this, Sound.BellRing) },
PlayBellRingAndWait: { execute: () => { throw new Error("Not Implemented yet."); } },
PlayMusic: { execute: () => { throw new Error("Not Implemented yet."); } },
Play: { execute: () => { throw new Error("Not Implemented yet."); } },
PlayAndWait: { execute: () => { throw new Error("Not Implemented yet."); } },
Pause: { execute: () => { throw new Error("Not Implemented yet."); } },
Stop: { execute: () => { throw new Error("Not Implemented yet."); } }
};

public readonly properties: { readonly [name: string]: LibraryPropertyInstance } = {};

public readonly events: { readonly [name: string]: LibraryEventInstance } = {};
}
19 changes: 19 additions & 0 deletions src/strings/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ export module DocumentationResources {
export const Shapes_HideShape_ShapeName = "The name of the shape.";
export const Shapes_ShowShape = "Shows a previously hidden shape.";
export const Shapes_ShowShape_ShapeName = "The name of the shape.";
export const Sound = "The Sound object provides operations that allow the playback of sounds. Some sample sounds are provided along with the library.";
export const Sound_PlayClick = "Plays the Click Sound.";
export const Sound_PlayClickAndWait = "Plays the Click Sound and waits for it to finish.";
export const Sound_PlayChime = "Plays the Chime Sound. ";
export const Sound_PlayChimeAndWait = "Plays the Chime Sound and waits for it to finish.";
export const Sound_PlayChimes = "Plays the Chimes Sound.";
export const Sound_PlayChimesAndWait = "Plays the Chimes Sound and waits for it to finish.";
export const Sound_PlayBellRing = "Plays the Bell Ring Sound.";
export const Sound_PlayBellRingAndWait = "Plays the Bell Ring Sound and waits for it to finish.";
export const Sound_PlayMusic = "Plays musical notes.";
export const Sound_PlayMusic_Notes = "A set of musical notes to play. The format is a subset of the Music Macro Language supported by QBasic.";
export const Sound_Play = "Plays an audio file. This could be an mp3 or wav or wma file. Other file formats may or may not play depending on the audio codecs installed on the user's computer. If the file was already paused, this operation will resume from the position where the playback was paused.";
export const Sound_Play_FilePath = "The path for the audio file. This could either be a local file (e.g.: c:\music\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).";
export const Sound_PlayAndWait = "Plays an audio file and waits until it is finished playing. This could be an mp3, wav, or wma file. Other file formats may or may not play depending on the audio codecs installed on the user's computer. If the file was already paused, this operation will resume from the position where the playback was paused.";
export const Sound_PlayAndWait_FilePath = "The path for the audio file. This could either be a local file (e.g.: c:\music\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).";
export const Sound_Pause = "Pauses playback of an audio file. If the file was not already playing, this operation will not do anything.";
export const Sound_Pause_FilePath = "The path for the audio file. This could either be a local file (e.g.: c:\music\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).";
export const Sound_Stop = "Stops playback of an audio file. If the file was not already playing, this operation will not do anything.";
export const Sound_Stop_FilePath = "The path for the audio file. This could either be a local file (e.g.: c:\music\track1.mp3) or a file on the network (e.g.: http://contoso.com/track01.wma).";
export const Stack = "This object provides a way of storing values just like stacking up a plate. You can push a value to the top of the stack and pop it off. You can only pop the values one by one off the stack and the last pushed value will be the first one to pop out.";
export const Stack_PushValue = "Pushes a value to the specified stack.";
export const Stack_PushValue_StackName = "The name of the stack.";
Expand Down
14 changes: 14 additions & 0 deletions tests/compiler/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CompilerPosition, CompilerRange } from "../../src/compiler/syntax/range
import { TokenKind } from "../../src/compiler/syntax/tokens";
import { ITextWindowLibraryPlugin, TextWindowColor } from "../../src/compiler/runtime/libraries/text-window";
import { BaseValue } from "../../src/compiler/runtime/values/base-value";
import { ISoundLibraryPlugin } from "../../src/compiler/runtime/libraries/sound";

export function getMarkerPosition(text: string, marker: string): CompilerPosition {
expect(marker.length).toBe(1);
Expand Down Expand Up @@ -162,3 +163,16 @@ export class TextWindowTestBuffer implements ITextWindowLibraryPlugin {
}
}
}

export class TestSoundLibraryPlugin implements ISoundLibraryPlugin {

private lastAudioPlayed : string = "";

public getLastAudioPlayed() : string {
return this.lastAudioPlayed;
}

public playAudio(audioFile: string): void {
this.lastAudioPlayed = audioFile;
}
}
67 changes: 67 additions & 0 deletions tests/compiler/runtime/libraries/sound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import "jasmine";
import { Compilation } from "../../../../src/compiler/compilation";
import { ExecutionEngine, ExecutionMode, ExecutionState } from "../../../../src/compiler/execution-engine";
import { TestSoundLibraryPlugin } from "../../helpers";
import { ClickSound, ChimeSound, ChimesSound, BellRingSound } from "../../../../src/compiler/runtime/libraries/sound";

describe("Compiler.Runtime.Libraries.Sound", () => {
it("plays a clicking sound", () => {
const compilation = new Compilation(`
Sound.PlayClick()`);

const plugin = new TestSoundLibraryPlugin();
const engine = new ExecutionEngine(compilation);

engine.libraries.Sound.plugin = plugin;
engine.execute(ExecutionMode.RunToEnd);

expect(plugin.getLastAudioPlayed()).toBe(ClickSound);
expect(engine.state).toBe(ExecutionState.Terminated);
expect(engine.exception).toBeUndefined();
});

it("plays a chiming sound", () => {
const compilation = new Compilation(`
Sound.PlayChime()`);

const plugin = new TestSoundLibraryPlugin();
const engine = new ExecutionEngine(compilation);

engine.libraries.Sound.plugin = plugin;
engine.execute(ExecutionMode.RunToEnd);

expect(plugin.getLastAudioPlayed()).toBe(ChimeSound);
expect(engine.state).toBe(ExecutionState.Terminated);
expect(engine.exception).toBeUndefined();
});

it("plays the chimes sound", () => {
const compilation = new Compilation(`
Sound.PlayChimes()`);

const plugin = new TestSoundLibraryPlugin();
const engine = new ExecutionEngine(compilation);

engine.libraries.Sound.plugin = plugin;
engine.execute(ExecutionMode.RunToEnd);

expect(plugin.getLastAudioPlayed()).toBe(ChimesSound);
expect(engine.state).toBe(ExecutionState.Terminated);
expect(engine.exception).toBeUndefined();
});

it("plays a ringing bell sound", () => {
const compilation = new Compilation(`
Sound.PlayBellRing()`);

const plugin = new TestSoundLibraryPlugin();
const engine = new ExecutionEngine(compilation);

engine.libraries.Sound.plugin = plugin;
engine.execute(ExecutionMode.RunToEnd);

expect(plugin.getLastAudioPlayed()).toBe(BellRingSound);
expect(engine.state).toBe(ExecutionState.Terminated);
expect(engine.exception).toBeUndefined();
});
});
1 change: 1 addition & 0 deletions tests/compiler/tests-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import "./runtime/libraries/array";
import "./runtime/libraries/clock";
import "./runtime/libraries/math";
import "./runtime/libraries/program";
import "./runtime/libraries/sound";
import "./runtime/libraries/stack";
import "./runtime/libraries/text-window";

Expand Down