├── favicon.ico ├── dev ├── images │ └── device_concept.png ├── midi_files │ ├── pachelbel-canon.mid │ ├── MarbleMachineLeftHand.mid │ ├── MarbleMachineRightHand.mid │ └── MarbleMachinePianoVersionBothHands.mid ├── midi_libraries │ ├── index.html │ ├── styles │ │ └── styles.css │ ├── MidiConvert.html │ └── midi_parser.html └── UML │ └── Application model.xml ├── experimental ├── whitebird │ ├── a.mid │ ├── favicon.ico │ ├── custom.css │ └── FileSaver.min.js ├── sandbox │ ├── images │ │ ├── page.png │ │ ├── strip.png │ │ ├── screen.png │ │ ├── chBck_invalid.gif │ │ ├── chBck_valid.gif │ │ ├── channel_background.gif │ │ └── screen_collapsed.png │ ├── midi │ │ └── pachelbel-canon.mid │ ├── inc │ │ ├── scalemodes.js │ │ ├── scalesandnotes.js │ │ ├── scales.js │ │ └── devices.json │ ├── templates │ │ ├── A4-01.svg │ │ ├── strip_dimensions.xml │ │ ├── strip_event_orientations │ │ ├── object_dimensions.xml │ │ ├── layout_anchors_2.xml │ │ └── layout_anchors │ ├── styles │ │ ├── styles.css │ │ └── device_concept.css │ └── index.html └── musicboxeditor │ ├── typings.json │ ├── src │ ├── assets │ │ ├── favicon.png │ │ ├── fonts │ │ │ ├── opensans.woff2 │ │ │ └── source │ │ │ │ └── segoeui.ttf │ │ └── icons │ │ │ └── source │ │ │ ├── interactor-font-icons │ │ │ ├── fonts │ │ │ │ ├── interactor-icons.ttf │ │ │ │ └── interactor-icons.woff │ │ │ ├── Read Me.txt │ │ │ ├── demo-files │ │ │ │ ├── demo.js │ │ │ │ └── demo.css │ │ │ └── style.css │ │ │ ├── square.svg │ │ │ ├── chevron-down.svg │ │ │ ├── chevron-right.svg │ │ │ ├── chevron-up.svg │ │ │ ├── chevron-left.svg │ │ │ ├── circle.svg │ │ │ ├── message.svg │ │ │ ├── delete.svg │ │ │ ├── file.svg │ │ │ ├── menu.svg │ │ │ ├── checkbox-old.svg │ │ │ ├── folder.svg │ │ │ ├── package.svg │ │ │ ├── checkbox-o.svg │ │ │ ├── alert-circle.svg │ │ │ ├── info-circle.svg │ │ │ ├── checkbox-minus.svg │ │ │ ├── account.svg │ │ │ ├── checkbox-plus.svg │ │ │ ├── folder-open.svg │ │ │ ├── radiobox.svg │ │ │ ├── approve.svg │ │ │ ├── save.svg │ │ │ ├── package-open.svg │ │ │ ├── asterisk.svg │ │ │ ├── exit.svg │ │ │ ├── medical-bag.svg │ │ │ ├── gender-female.svg │ │ │ ├── refresh.svg │ │ │ ├── gender-male.svg │ │ │ ├── radiobox-marked.svg │ │ │ ├── cross.svg │ │ │ ├── search.svg │ │ │ ├── gender-male-female.svg │ │ │ ├── checkbox-inc-old.svg │ │ │ ├── gender-transgender.svg │ │ │ ├── checkbox-inc.svg │ │ │ ├── checkbox.svg │ │ │ ├── script │ │ │ ├── iconFont.js │ │ │ └── template.css │ │ │ └── persons.svg │ ├── app │ │ ├── ui │ │ │ ├── contextMenu │ │ │ │ ├── contextMenuRenderer.component.html │ │ │ │ ├── contextMenu.interface.ts │ │ │ │ ├── contextMenu.component.html │ │ │ │ ├── contextMenuRenderer.component.css │ │ │ │ ├── contextMenu.component.css │ │ │ │ ├── contextMenu.directive.ts │ │ │ │ ├── contextMenu.module.ts │ │ │ │ ├── contextMenuRenderer.component.ts │ │ │ │ ├── contextMenu.component.ts │ │ │ │ └── contextMenu.service.ts │ │ │ ├── hint │ │ │ │ ├── hint.component.html │ │ │ │ ├── hint.component.css │ │ │ │ ├── hint.module.ts │ │ │ │ ├── hint.service.ts │ │ │ │ ├── hint.directive.ts │ │ │ │ ├── hint.spec.ts │ │ │ │ └── hint.component.ts │ │ │ ├── tabMenu │ │ │ │ ├── tabMenu.component.html │ │ │ │ ├── tabMenu.interface.ts │ │ │ │ ├── tabMenu.module.ts │ │ │ │ ├── tabMenu.component.css │ │ │ │ ├── tabMenu.component.ts │ │ │ │ └── tabMenu.spec.ts │ │ │ ├── subMenu │ │ │ │ ├── subMenu.component.html │ │ │ │ ├── subMenu.interface.ts │ │ │ │ ├── subMenu.module.ts │ │ │ │ ├── subMenu.component.css │ │ │ │ ├── subMenu.component.ts │ │ │ │ └── subMenu.spec.ts │ │ │ └── dialog │ │ │ │ ├── dialog.interface.ts │ │ │ │ ├── dialog.module.ts │ │ │ │ ├── dialog.component.html │ │ │ │ ├── dialog.service.ts │ │ │ │ ├── dialog.component.css │ │ │ │ └── dialog.component.ts │ │ ├── service │ │ │ ├── log │ │ │ │ ├── log.module.ts │ │ │ │ └── log.service.ts │ │ │ ├── mtosState │ │ │ │ ├── mtosState.module.ts │ │ │ │ └── mtosState.service.ts │ │ │ └── input.service.ts │ │ ├── directive │ │ │ └── dragAndDrop │ │ │ │ ├── dragAndDrop.module.ts │ │ │ │ ├── dragAndDrop.service.ts │ │ │ │ └── dragAndDrop.directive.ts │ │ ├── app.module.ts │ │ ├── app.component.css │ │ └── app.component.html │ ├── index.html │ ├── main.jit.ts │ ├── main.aot.ts │ ├── main.test.ts │ ├── styles.css │ └── typings.d.ts │ ├── dist │ ├── assets │ │ ├── favicon.png │ │ └── fonts │ │ │ └── opensans.woff2 │ ├── index.html │ └── styles.css │ ├── .gitignore │ ├── tsconfig.json │ ├── lite-server.config.js │ ├── TODO.md │ ├── README.md │ ├── karma.config.js │ ├── package.json │ └── webpack.config.js ├── inc ├── custom.css ├── processing.js ├── scaleandmusicbox.js ├── drawing.js └── user-interface.js ├── README.md ├── CONTRIBUTING.md └── LICENSE.md /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/favicon.ico -------------------------------------------------------------------------------- /dev/images/device_concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/dev/images/device_concept.png -------------------------------------------------------------------------------- /experimental/whitebird/a.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/whitebird/a.mid -------------------------------------------------------------------------------- /dev/midi_files/pachelbel-canon.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/dev/midi_files/pachelbel-canon.mid -------------------------------------------------------------------------------- /experimental/whitebird/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/whitebird/favicon.ico -------------------------------------------------------------------------------- /experimental/sandbox/images/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/images/page.png -------------------------------------------------------------------------------- /experimental/sandbox/images/strip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/images/strip.png -------------------------------------------------------------------------------- /dev/midi_files/MarbleMachineLeftHand.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/dev/midi_files/MarbleMachineLeftHand.mid -------------------------------------------------------------------------------- /experimental/sandbox/images/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/images/screen.png -------------------------------------------------------------------------------- /dev/midi_files/MarbleMachineRightHand.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/dev/midi_files/MarbleMachineRightHand.mid -------------------------------------------------------------------------------- /experimental/musicboxeditor/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDependencies": { 3 | "core-js": "registry:dt/core-js#0.0.0+20160914114559" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /experimental/sandbox/images/chBck_invalid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/images/chBck_invalid.gif -------------------------------------------------------------------------------- /experimental/sandbox/images/chBck_valid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/images/chBck_valid.gif -------------------------------------------------------------------------------- /experimental/sandbox/midi/pachelbel-canon.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/midi/pachelbel-canon.mid -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/musicboxeditor/src/assets/favicon.png -------------------------------------------------------------------------------- /experimental/sandbox/images/channel_background.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/images/channel_background.gif -------------------------------------------------------------------------------- /experimental/sandbox/images/screen_collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/sandbox/images/screen_collapsed.png -------------------------------------------------------------------------------- /experimental/musicboxeditor/dist/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/musicboxeditor/dist/assets/favicon.png -------------------------------------------------------------------------------- /dev/midi_files/MarbleMachinePianoVersionBothHands.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/dev/midi_files/MarbleMachinePianoVersionBothHands.mid -------------------------------------------------------------------------------- /experimental/musicboxeditor/.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode/ 2 | /build/ 3 | /node_modules/ 4 | /typings/ 5 | /.idea/ 6 | *.css.shim.ts 7 | *.ngfactory.ts 8 | *.mid 9 | *.log 10 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/dist/assets/fonts/opensans.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/musicboxeditor/dist/assets/fonts/opensans.woff2 -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/fonts/opensans.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/musicboxeditor/src/assets/fonts/opensans.woff2 -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/fonts/source/segoeui.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/musicboxeditor/src/assets/fonts/source/segoeui.ttf -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenuRenderer.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/hint/hint.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/interactor-font-icons/fonts/interactor-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/musicboxeditor/src/assets/icons/source/interactor-font-icons/fonts/interactor-icons.ttf -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/interactor-font-icons/fonts/interactor-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wintergatan/Midi-to-laser-cutter/HEAD/experimental/musicboxeditor/src/assets/icons/source/interactor-font-icons/fonts/interactor-icons.woff -------------------------------------------------------------------------------- /inc/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | /* Required padding for .navbar-fixed-top. Remove if using .navbar-static-top. Change if height of navigation changes. */ 4 | } 5 | 6 | #export-row { 7 | padding-top: 20px; 8 | } 9 | 10 | #download-btn { 11 | margin: 20px 0; 12 | } 13 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenu.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IContextMenuOptions { 2 | width?: number; 3 | height?: number; 4 | x?: number; 5 | y?: number; 6 | text?: string; 7 | icon?: string; 8 | menu?: IContextMenuOptions[]; 9 | callback?: ()=>void; 10 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/tabMenu/tabMenu.component.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/tabMenu/tabMenu.interface.ts: -------------------------------------------------------------------------------- 1 | import { TabMenuComponent } from './tabMenu.component'; 2 | 3 | export interface ITabMenuButton { 4 | text: string; 5 | callback?: ()=>boolean; 6 | } 7 | 8 | export interface ITabMenuComponentOptions { 9 | component?: TabMenuComponent; 10 | onInit?: ()=>void; 11 | buttons?: ITabMenuButton[]; 12 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "experimentalDecorators": true, 7 | "emitDecoratorMetadata": true, 8 | "sourceMap": true 9 | }, 10 | "compileOnSave": false, 11 | "angularCompilerOptions": { 12 | "skipMetadataEmit": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/chevron-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/chevron-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/chevron-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/chevron-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/checkbox-old.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/package.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/subMenu/subMenu.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/checkbox-o.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/alert-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/info-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/service/log/log.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { LogService, Logger } from './log.service'; 3 | 4 | export { LogService, Logger }; 5 | 6 | @NgModule({}) 7 | export class LogModule { 8 | static forRoot(): ModuleWithProviders { 9 | return { 10 | ngModule: LogModule, 11 | providers: [ LogService ] 12 | }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/checkbox-minus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Musicbox Editor 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/account.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/tabMenu/tabMenu.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { TabMenuComponent } from './tabMenu.component'; 4 | 5 | export { TabMenuComponent }; 6 | export * from './tabMenu.interface'; 7 | 8 | @NgModule({ 9 | imports: [CommonModule], 10 | declarations: [TabMenuComponent], 11 | exports: [TabMenuComponent] 12 | }) 13 | export class TabMenuModule { } 14 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/checkbox-plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/folder-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/radiobox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/approve.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/save.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenu.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 | 7 |
-------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/package-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /dev/midi_libraries/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MIDI Library Tests 6 | 7 | 8 | 9 | 10 |
11 | Homemidi-parser.jsMidiConvert.js 12 |

Simple pages to test the output in JSON format of each midi parsing library

13 |
14 | 15 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/dialog/dialog.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IDialogButton { 2 | text?: string; 3 | disableClose?: boolean; 4 | callback?: ()=>void; 5 | }; 6 | 7 | export interface IDialogSelect { 8 | text?: string; 9 | selected?: any; 10 | options?: any; //{key: string, value: string}; 11 | }; 12 | 13 | export interface IDialog { 14 | heading?: string; 15 | content?: string; 16 | level?: string; 17 | buttons?: IDialogButton[]; 18 | select?: IDialogSelect[]; 19 | }; -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/asterisk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/exit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/medical-bag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/service/mtosState/mtosState.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { MtosStateService, IHeader, INote, ITrack } from "./mtosState.service"; 3 | 4 | export { MtosStateService, IHeader, INote, ITrack }; 5 | 6 | @NgModule({}) 7 | export class MtosStateModule { 8 | static forRoot(): ModuleWithProviders { 9 | return { 10 | ngModule: MtosStateModule, 11 | providers: [ MtosStateService ] 12 | }; 13 | } 14 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/hint/hint.component.css: -------------------------------------------------------------------------------- 1 | :host{ 2 | pointer-events: none; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | width: 0; 7 | height: 0; 8 | overflow: hidden; 9 | } 10 | 11 | .box { 12 | position: fixed; 13 | padding: 0.5em; 14 | max-width: 20em; 15 | color: #444444; 16 | background-color: white; 17 | border: 1px solid #e0e0e0; 18 | box-shadow: rgba(0, 0, 0, 0.1) 2px 2px 0 0; 19 | /*box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.25);*/ 20 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/subMenu/subMenu.interface.ts: -------------------------------------------------------------------------------- 1 | import { SubMenuComponent } from './subMenu.component'; 2 | 3 | export interface ISubMenuButton { 4 | text: string; 5 | callback?: ()=>void; 6 | iconClass?: string; 7 | } 8 | 9 | export interface ISubMenuComponentOptions { 10 | component?: SubMenuComponent; 11 | onInit?: ()=>void; 12 | disableBackground?: boolean; 13 | disableIcons?: boolean; 14 | disableText?: boolean; 15 | iconSize?: string; 16 | buttons?: ISubMenuButton[]; 17 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/gender-female.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/subMenu/subMenu.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { HintModule } from '../hint/hint.module'; 5 | import { SubMenuComponent } from './subMenu.component'; 6 | 7 | export * from './subMenu.interface'; 8 | export { SubMenuComponent }; 9 | 10 | @NgModule({ 11 | imports: [CommonModule, HintModule ], 12 | declarations: [SubMenuComponent], 13 | exports: [SubMenuComponent] 14 | }) 15 | export class SubMenuModule { } 16 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/gender-male.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/radiobox-marked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Musicbox Editor 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/tabMenu/tabMenu.component.css: -------------------------------------------------------------------------------- 1 | .tab-menu { 2 | display: flex; 3 | flex-wrap: wrap; 4 | } 5 | 6 | .button { 7 | display: flex; 8 | padding: 0.2em 0.5em; 9 | color: #888888; 10 | } 11 | 12 | .button:hover{ 13 | border-bottom: 3px solid ; 14 | } 15 | 16 | .button+.button{ 17 | margin-left: 0.5em; 18 | } 19 | 20 | .isSelected { 21 | color: #028478; 22 | border-bottom: 3px solid #028478; 23 | } 24 | 25 | button { 26 | box-sizing: content-box; 27 | background-color: transparent; 28 | border-bottom: 3px solid transparent; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/gender-male-female.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/whitebird/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | /* Required padding for .navbar-fixed-top. Remove if using .navbar-static-top. Change if height of navigation changes. */ 4 | } 5 | 6 | #export-row { 7 | padding-top: 20px; 8 | } 9 | 10 | #download-btn { 11 | margin: 20px 0; 12 | } 13 | 14 | #preview-box { 15 | margin-top:20px; 16 | } 17 | 18 | #preview-wrapper { 19 | overflow-y: hidden; 20 | overflow-x: scroll; 21 | } 22 | 23 | #preview { 24 | width:100%; 25 | height:98%; 26 | border: 1px solid black; 27 | } 28 | 29 | .note-selector { 30 | margin-bottom:6px; 31 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Midi to laser cutter 2 | This repository will host the code for the future midi to laser cutter converter. 3 | 4 | - General chatting can be done at [Gitter](https://gitter.im/Wintergatan/Midi-to-laser-cutter) 5 | - Slow discussions can be started in the [Issue tracker](https://github.com/Wintergatan/Midi-to-laser-cutter/issues) 6 | - See current version here: [Link](https://wintergatan.github.io/Midi-to-laser-cutter/) 7 | - Prototype B here (Supports Chrome and Firefox): [Link](https://wintergatan.github.io/Midi-to-laser-cutter/experimental/musicboxeditor/dist/) 8 | - [Info on how to contribute](https://github.com/Wintergatan/Midi-to-laser-cutter/blob/master/CONTRIBUTING.md) 9 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/checkbox-inc-old.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/main.jit.ts: -------------------------------------------------------------------------------- 1 | 2 | import "reflect-metadata"; //only needed for jit. 3 | 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { AppModule } from './app/app.module'; 6 | 7 | 8 | console.info('app.environment:', app.environment); 9 | if(document.readyState === "complete" || document.readyState === "interactive") { 10 | console.log('Bootstrapping app'); 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | } 13 | else { 14 | document.addEventListener("DOMContentLoaded", function() { 15 | console.log('Bootstrapping app'); 16 | platformBrowserDynamic().bootstrapModule(AppModule); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/lite-server.config.js: -------------------------------------------------------------------------------- 1 | const proxyMiddleware = require('http-proxy-middleware'); 2 | 3 | const useGzip = process.env.APP_ENVIRONMENT == 'gzip'; 4 | 5 | var config = { 6 | "port": 9100, 7 | "files": ["./build/**/*.{html,htm,css,js}"], 8 | "server": { 9 | "baseDir": "./build", 10 | middleware: { 11 | 1: proxyMiddleware('/interactor', { 12 | target: 'http://localhost:8080/', 13 | changeOrigin: true 14 | }) 15 | } 16 | } 17 | }; 18 | 19 | /* 20 | if(useGzip){ 21 | 22 | config.server.middleware = { 1 : require('compression')() }; 23 | } 24 | */ 25 | 26 | 27 | module.exports = config; -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenuRenderer.component.css: -------------------------------------------------------------------------------- 1 | :host{ 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | pointer-events: none; 8 | } 9 | 10 | :host>*{ 11 | pointer-events: initial; 12 | } 13 | 14 | :host *{ 15 | user-select: none; 16 | cursor: default; 17 | box-sizing: border-box; 18 | } 19 | 20 | .context-menu{ 21 | position: fixed; 22 | padding: 0.5em 0; 23 | border: 1px solid #444444; 24 | background-color: #ffffff; 25 | overflow-y: auto; 26 | } 27 | 28 | .element{ 29 | padding: 0.25em 0.5em; 30 | } 31 | 32 | .element:hover{ 33 | background-color: rgba(0, 0, 0, 0.1); 34 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/hint/hint.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { HintService } from './hint.service'; 5 | import { HintComponent } from './hint.component'; 6 | import { Hint } from './hint.directive'; 7 | 8 | export { HintComponent, Hint }; 9 | 10 | @NgModule({ 11 | imports: [CommonModule], 12 | declarations: [Hint, HintComponent], 13 | exports: [Hint, HintComponent] 14 | }) 15 | export class HintModule { 16 | static forRoot(): ModuleWithProviders { 17 | return { 18 | ngModule: HintModule, 19 | providers: [ HintService ] 20 | }; 21 | } 22 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/gender-transgender.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/checkbox-inc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/checkbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/service/input.service.ts: -------------------------------------------------------------------------------- 1 | 2 | export class InputService { 3 | 4 | static keyCode = { 5 | Enter: 13, 6 | ArrowUp: 38, 7 | ArrowDown: 40, 8 | ArrowLeft: 37, 9 | ArrowRight: 39 10 | }; 11 | 12 | static isKeyDown(event: KeyboardEvent ,keyCode: number){ 13 | if(event.keyCode === keyCode){ 14 | return true; 15 | } 16 | 17 | if(event.which === keyCode){ 18 | return true; 19 | } 20 | 21 | if(InputService.keyCode[event.code] === keyCode){ 22 | return true; 23 | } 24 | 25 | if(InputService.keyCode[event.key] === keyCode){ 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/main.aot.ts: -------------------------------------------------------------------------------- 1 | 2 | import "reflect-metadata"; //only needed for jit. 3 | 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { enableProdMode } from '@angular/core'; 6 | import { AppModule } from './app/app.module'; 7 | 8 | console.info('app.environment:', app.environment); 9 | if(document.readyState === "complete" || document.readyState === "interactive") { 10 | console.log('Bootstrapping app'); 11 | enableProdMode(); 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | } 14 | else { 15 | document.addEventListener("DOMContentLoaded", function() { 16 | console.log('Bootstrapping app'); 17 | enableProdMode(); 18 | platformBrowserDynamic().bootstrapModule(AppModule); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/hint/hint.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import { HintComponent } from './hint.component'; 3 | 4 | @Injectable() 5 | export class HintService { 6 | public renderer: HintComponent = null; 7 | 8 | constructor() { } 9 | 10 | public setRenderer(renderer: HintComponent) { 11 | this.renderer = renderer; 12 | } 13 | 14 | public open(hint: string, bbox: ClientRect): void { 15 | if (this.renderer) { 16 | this.renderer.open(hint, bbox); 17 | } else { 18 | console.log('Hint renderer not found, is it added to template?'); 19 | } 20 | } 21 | 22 | public close(): void { 23 | if (this.renderer) { 24 | this.renderer.close(); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenu.component.css: -------------------------------------------------------------------------------- 1 | :host{ 2 | position: fixed; 3 | padding: 0.5em 0; 4 | color: #444444; 5 | background-color: white; 6 | border: 1px solid #444444; 7 | box-shadow: rgba(0, 0, 0, 0.1) 2px 2px 0 0; 8 | overflow-y: auto; 9 | } 10 | 11 | :host:focus{ 12 | outline: none; 13 | } 14 | 15 | .element{ 16 | padding: 0.25em 0.5em; 17 | display: flex; 18 | align-items: center; 19 | } 20 | 21 | .element:hover{ 22 | background-color: rgba(0, 0, 0, 0.1); 23 | } 24 | 25 | .icon{ 26 | display: block; 27 | width: 1em; 28 | } 29 | 30 | .text{ 31 | margin-left: 0.5em; 32 | } 33 | 34 | .space{ 35 | flex: 100 100 50%; 36 | min-width: 0; 37 | } 38 | 39 | .sub-menu-icon{ 40 | display: block; 41 | width: 1em; 42 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | ## Multipart download 4 | * Option for multipart download in gui (checkbox or select). 5 | 6 | ## Scale options 7 | * more options? 8 | 9 | ## Add note 10 | * In track view. 11 | * Show ghost note on hover. 12 | * On click add note and sort notes. 13 | 14 | 15 | ## Move note 16 | * In track view. 17 | * On click down, remove note. 18 | * Show ghost note. 19 | * On click up add ghost note and sort notes. 20 | * Hold shift for snap to grid. 21 | * If action is not completed (drag outside of screen). Add note to old position. 22 | 23 | 24 | ## Show more info 25 | * Number of notes 26 | * Number of track parts. 27 | * Length of track in mm. 28 | * Colliding notes -> orange? 29 | 30 | 31 | ## Extra options 32 | * Show and hide grid. 33 | * Show and hide track part number 34 | 35 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/directive/dragAndDrop/dragAndDrop.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { DragAndDropService } from "./dragAndDrop.service"; 3 | import { DragStartDirective, DragOverDirective, DropDirective, DragSuccessDirective } from "./dragAndDrop.directive"; 4 | 5 | export { DragAndDropService }; 6 | 7 | @NgModule({ 8 | declarations: [DragStartDirective, DragSuccessDirective, DropDirective, DragOverDirective], 9 | exports: [DragStartDirective, DragSuccessDirective, DropDirective, DragOverDirective], 10 | }) 11 | export class DragAndDropModule { 12 | static forRoot(): ModuleWithProviders { 13 | return { 14 | ngModule: DragAndDropModule, 15 | providers: [ DragAndDropService ] 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/interactor-font-icons/Read Me.txt: -------------------------------------------------------------------------------- 1 | Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures. 2 | 3 | To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/#docs/local-fonts 4 | 5 | You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects. 6 | 7 | You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection. 8 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/dialog/dialog.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { DialogComponent } from './dialog.component'; 5 | import { DialogService } from './dialog.service'; 6 | 7 | 8 | export { DialogService, DialogComponent }; 9 | export * from './dialog.interface'; //<- this do not work in plunker for some reason. But you get the idea. 10 | import { FormsModule } from "@angular/forms"; 11 | 12 | 13 | @NgModule({ 14 | imports: [ CommonModule, FormsModule ], 15 | declarations: [ DialogComponent ], 16 | exports: [ DialogComponent ] 17 | }) 18 | export class DialogModule { 19 | static forRoot() { 20 | return { 21 | ngModule: DialogModule, 22 | providers: [ DialogService ] 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/directive/dragAndDrop/dragAndDrop.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class DragAndDropService { 5 | 6 | private success: boolean; 7 | private data: any; 8 | 9 | constructor() { 10 | this.data = {}; 11 | this.success = false; 12 | } 13 | 14 | clear() { 15 | this.data = {}; 16 | this.success = false; 17 | } 18 | 19 | setData(type: string, data: any) { 20 | this.data[type] = data; 21 | } 22 | 23 | getData(type: string): any | null { 24 | if (this.data[type]) { 25 | return this.data[type]; 26 | } 27 | return null; 28 | } 29 | 30 | setSuccess(success: boolean){ 31 | this.success = true; 32 | } 33 | 34 | getSuccess():boolean{ 35 | return this.success; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/subMenu/subMenu.component.css: -------------------------------------------------------------------------------- 1 | .sub-menu { 2 | display: flex; 3 | flex-wrap: wrap; 4 | background-color: #e0e0e0; 5 | } 6 | 7 | .disableBackground { 8 | background-color: transparent; 9 | } 10 | 11 | .button { 12 | padding: 0.2em 0.75em; 13 | color: white; 14 | /*color: #444444;*/ 15 | user-select: none; 16 | cursor: default; 17 | } 18 | 19 | .button+.button{ 20 | margin-left: 0.5em; 21 | } 22 | 23 | .button:hover { 24 | background-color: rgba(0, 0, 0, 0.10); 25 | } 26 | 27 | .button:active { 28 | background-color: rgba(0, 0, 0, 0.2); 29 | } 30 | 31 | button { 32 | display: flex; 33 | align-items: center; 34 | height: 2.25em; 35 | background-color: transparent; 36 | } 37 | 38 | button *+*{ 39 | margin-left: 0.25em; 40 | } 41 | 42 | i { 43 | font-size: 1.25em; 44 | } 45 | 46 | i.big { 47 | font-size: 1.5em; 48 | } 49 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/dialog/dialog.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
{{dialog.heading}}
6 |
{{dialog.content}}
7 |
8 | 14 |
15 |
16 | 17 |
18 |
19 |
20 |
-------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Wintergatan midi to laser cutter 2 | 3 | ## Issue Contributions 4 | 5 | When opening issues, search for it to see if it doesn't already exist. 6 | 7 | ## Code Contributions 8 | 9 | Contact [whitebird](https://github.com/whitebird) if you wish to join the organisation to contribute code. This is also possible without joining, but mention that to him. You can contact him on Gitter : https://gitter.im/Wintergatan/Midi-to-laser-cutter 10 | Before adding a feature, check the To-Do list at the project overview: https://github.com/Wintergatan/Midi-to-laser-cutter/projects/2 11 | 12 | [Specific information about the experimental version](https://github.com/Wintergatan/Midi-to-laser-cutter/blob/master/experimental/musicboxeditor/README.md) 13 | 14 | There are currently two versions: main and experimental. THe experimental version has the most features at the moment and is most actively being developed. 15 | Before adding a feature, mention it in the Gitter lobby. 16 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenu.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostListener, Input } from '@angular/core'; 2 | import { ContextMenuService } from "./contextMenu.service"; 3 | import { IContextMenuOptions } from "./contextMenu.interface"; 4 | 5 | 6 | @Directive({ 7 | selector: '[context-menu]' 8 | }) 9 | export class ContextMenuDirective { 10 | @Input('context-menu') private options: IContextMenuOptions; 11 | 12 | constructor(private contextMenuService: ContextMenuService) { } 13 | 14 | @HostListener('contextmenu', ['$event']) 15 | onMouseEnter(event: MouseEvent) { 16 | if (this.options) { 17 | this.options.x = event.clientX; 18 | this.options.y = event.clientY; 19 | this.contextMenuService.openContextMenu(this.options); 20 | event.preventDefault(); 21 | } 22 | } 23 | 24 | @HostListener('mousedown') 25 | onMouseDown() { 26 | this.contextMenuService.closeContextMenu(); 27 | } 28 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenu.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ContextMenuComponent } from "./contextMenu.component"; 4 | import { ContextMenuService } from "./contextMenu.service"; 5 | import { ContextMenuDirective } from "./contextMenu.directive"; 6 | import { ContextMenuRendererComponent } from "./contextMenuRenderer.component"; 7 | 8 | 9 | export { ContextMenuDirective, ContextMenuComponent, ContextMenuService } 10 | export * from './contextMenu.interface'; 11 | 12 | @NgModule({ 13 | imports: [ CommonModule ], 14 | declarations: [ ContextMenuComponent, ContextMenuRendererComponent, ContextMenuDirective ], 15 | exports: [ ContextMenuComponent, ContextMenuRendererComponent, ContextMenuDirective ] 16 | }) 17 | export class ContextMenuModule { 18 | static forRoot() { 19 | return { 20 | ngModule: ContextMenuModule, 21 | providers: [ ContextMenuService ] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /experimental/sandbox/inc/scalemodes.js: -------------------------------------------------------------------------------- 1 | 2 | // Can anyone fill in the blanks?? 3 | 4 | var scaleModes = { 5 | major: { 6 | ionian: [2,2,1,2,2,2,1], // last 1 added for 'wrap around' 7 | dorian: [2,1,2,2,2,1,2], 8 | phyrigian: [1,2,2,2,1,2,2], 9 | lydian: [2,2,2,1,2,2,1], 10 | mixolydian: [2,2,1,2,2,1,2], 11 | aeolian: [2,1,2,2,1,2,2], 12 | locrian: [1,2,2,1,2,2,2] 13 | }, 14 | harmonic_minor: { 15 | harmminor: [2,1,2,2,1,3,1], 16 | locriansharpsix:[1,2,2,1,3,1,2], 17 | ionianaug: [2,2,1,3,1,2,1], 18 | romanian: [2,1,3,1,2,1,2], 19 | phrygiandom: [1,3,1,2,1,2,2], 20 | lydiansharptwo: [3,1,2,1,2,2,1], 21 | ultralocrian: [1,2,1,2,2,1,3] 22 | }, 23 | melodic_minor: { 24 | jazz_minor: [2,1,2,2,2,2,1], 25 | dorianflatnine: [1,2,2,2,2,1,2], 26 | lydianaug: [2,2,2,2,1,2,1], 27 | lydiandom: [2,2,2,1,2,1,2], 28 | mixolydianflatsix: [2,2,1,2,1,2,2], 29 | semilocrian: [2,1,2,1,2,2,2], 30 | superlocrian: [1,2,1,2,2,2,2] 31 | } 32 | }; -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Wintergatan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/main.test.ts: -------------------------------------------------------------------------------- 1 | /*when hosting .html*/ 2 | /* 3 | import 'jasmine-core/lib/jasmine-core/jasmine.css'; 4 | import 'jasmine-core/lib/jasmine-core/jasmine.js'; 5 | import 'jasmine-core/lib/jasmine-core/jasmine-html.js'; 6 | import 'jasmine-core/lib/jasmine-core/boot.js'; 7 | */ 8 | 9 | import 'reflect-metadata'; 10 | import 'core-js/es6'; 11 | import 'core-js/es7/reflect'; 12 | 13 | import "zone.js/dist/zone"; 14 | import "zone.js/dist/long-stack-trace-zone"; 15 | import "zone.js/dist/async-test"; 16 | import "zone.js/dist/fake-async-test"; 17 | import "zone.js/dist/sync-test"; 18 | import "zone.js/dist/proxy"; 19 | import "zone.js/dist/jasmine-patch"; 20 | 21 | import 'rxjs/add/operator/map'; 22 | import 'rxjs/add/observable/of'; 23 | 24 | import { TestBed } from "@angular/core/testing"; 25 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting} from "@angular/platform-browser-dynamic/testing"; 26 | 27 | TestBed.initTestEnvironment( 28 | BrowserDynamicTestingModule, 29 | platformBrowserDynamicTesting()); 30 | 31 | let testContext = require.context('./app', true, /\.spec/); 32 | testContext.keys().forEach(testContext); -------------------------------------------------------------------------------- /experimental/sandbox/templates/A4-01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/interactor-font-icons/demo-files/demo.js: -------------------------------------------------------------------------------- 1 | if (!('boxShadow' in document.body.style)) { 2 | document.body.setAttribute('class', 'noBoxShadow'); 3 | } 4 | 5 | document.body.addEventListener("click", function(e) { 6 | var target = e.target; 7 | if (target.tagName === "INPUT" && 8 | target.getAttribute('class').indexOf('liga') === -1) { 9 | target.select(); 10 | } 11 | }); 12 | 13 | (function() { 14 | var fontSize = document.getElementById('fontSize'), 15 | testDrive = document.getElementById('testDrive'), 16 | testText = document.getElementById('testText'); 17 | function updateTest() { 18 | testDrive.innerHTML = testText.value || String.fromCharCode(160); 19 | if (window.icomoonLiga) { 20 | window.icomoonLiga(testDrive); 21 | } 22 | } 23 | function updateSize() { 24 | testDrive.style.fontSize = fontSize.value + 'px'; 25 | } 26 | fontSize.addEventListener('change', updateSize, false); 27 | testText.addEventListener('input', updateTest, false); 28 | testText.addEventListener('change', updateTest, false); 29 | updateSize(); 30 | }()); 31 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { FormsModule } from '@angular/forms'; 4 | 5 | import { AppComponent } from './app.component'; 6 | import { TabMenuModule, ITabMenuComponentOptions, ITabMenuButton } from './ui/tabMenu/tabMenu.module'; 7 | import { SubMenuModule } from './ui/subMenu/subMenu.module'; 8 | import { HintModule } from './ui/hint/hint.module'; 9 | import { DialogModule } from './ui/dialog/dialog.module'; 10 | 11 | import { DragAndDropModule } from "./directive/dragAndDrop/dragAndDrop.module"; 12 | 13 | 14 | import { LogModule } from './service/log/log.module'; 15 | import { ContextMenuModule } from "./ui/contextMenu/contextMenu.module"; 16 | import { MtosStateModule } from "./service/mtosState/mtosState.module"; 17 | 18 | @NgModule({ 19 | imports: [ 20 | BrowserModule, 21 | FormsModule, 22 | LogModule.forRoot(), 23 | MtosStateModule.forRoot(), 24 | DragAndDropModule.forRoot(), 25 | TabMenuModule, 26 | SubMenuModule, 27 | HintModule.forRoot(), 28 | DialogModule.forRoot(), 29 | ContextMenuModule.forRoot(), 30 | ], 31 | declarations: [AppComponent], 32 | bootstrap: [AppComponent] 33 | }) 34 | export class AppModule { 35 | } 36 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/hint/hint.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostListener, Input } from '@angular/core'; 2 | import { HintService } from './hint.service'; 3 | 4 | 5 | @Directive({ 6 | selector: '[hint]' 7 | }) 8 | export class Hint { 9 | @Input('hint') private hint: string; 10 | 11 | private timeout; 12 | 13 | constructor(private hintService: HintService) {} 14 | 15 | 16 | private openHint(bbox: ClientRect) { 17 | if (this.hintService) { 18 | this.hintService.open(this.hint, bbox); 19 | } 20 | } 21 | 22 | private closeHint() { 23 | if (this.hintService) { 24 | this.hintService.close(); 25 | } 26 | } 27 | 28 | @HostListener('mouseenter', ['$event']) 29 | onMouseEnter(event:MouseEvent) { 30 | if(this.hint && this.hint.trim() !== ''){ 31 | this.timeout = setTimeout(()=>{ 32 | this.openHint((event.target).getBoundingClientRect()); 33 | }, 200); 34 | } 35 | } 36 | 37 | @HostListener('mouseleave') 38 | onMouseLeave() { 39 | clearTimeout(this.timeout); 40 | this.closeHint(); 41 | } 42 | 43 | @HostListener('mousedown') 44 | onMouseDown() { 45 | clearTimeout(this.timeout); 46 | this.closeHint(); 47 | } 48 | } -------------------------------------------------------------------------------- /dev/midi_libraries/styles/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | font-size: 100%; 4 | margin: 0; 5 | padding: 0; 6 | background-color: #f2f2f2; 7 | } 8 | 9 | #container { 10 | position: relative; 11 | width: 85%; 12 | min-height: 720px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | margin-top: 20px; 16 | padding: 20px; 17 | } 18 | 19 | #controls { 20 | position: relative; 21 | width: 16%; 22 | float: left; 23 | } 24 | 25 | #output { 26 | position: relative; 27 | border: 1px solid darkgray; 28 | float: right; 29 | width: 75%; 30 | min-height: 680px; 31 | border-radius: 10px; 32 | background-color: white; 33 | padding: 20px; 34 | } 35 | 36 | h1 { 37 | margin-top: 0; 38 | font-size: 1em; 39 | } 40 | 41 | #controls p { 42 | font-size: 0.6em; 43 | } 44 | 45 | #json-view { 46 | position: relative; 47 | width: 99%; 48 | min-height: 680px; 49 | overflow: scroll; 50 | } 51 | header{ 52 | margin-top:10px; 53 | position: relative; 54 | width: 85%; 55 | height: 20px; 56 | margin-left:auto; 57 | margin-right:auto; 58 | padding-left: 25%; 59 | } 60 | header a{ 61 | 62 | display:inline-block; 63 | width: 150px; 64 | height: 40px; 65 | line-height: 40px; 66 | background-color: darkgray; 67 | color:white; 68 | text-align: center; 69 | margin-right: 20px; 70 | border-radius: 5px; 71 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/hint/hint.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { HintModule, HintComponent } from './hint.module'; 3 | import { HintService } from "./hint.service"; 4 | 5 | describe('HintComponent', () => { 6 | 7 | let fixture: ComponentFixture; 8 | let comp: HintComponent; 9 | let el: HTMLElement; 10 | let service: HintService; 11 | 12 | beforeEach(() => { 13 | 14 | TestBed.configureTestingModule({ 15 | imports: [ 16 | HintModule.forRoot() 17 | ] 18 | }); 19 | 20 | fixture = TestBed.createComponent(HintComponent); 21 | comp = fixture.componentInstance; 22 | el = fixture.debugElement.nativeElement; 23 | service = fixture.debugElement.injector.get(HintService); 24 | 25 | }); 26 | 27 | it('should create the HintComponent', () => { 28 | expect(comp).toBeTruthy(); 29 | }); 30 | 31 | 32 | it('should have default values', () => { 33 | expect(comp.hint).toBeUndefined(); 34 | expect(comp.pos.x).toBe(0); 35 | expect(comp.pos.y).toBe(0); 36 | expect(comp.bbox).toBeUndefined(); 37 | }); 38 | 39 | it('should display hint', ()=>{ 40 | service.open('This is a hint', {top: 0, left: 0, bottom: 100, right: 100, width: 100, height: 100 }) 41 | 42 | fixture.detectChanges(); 43 | 44 | expect(comp.hint).toBe('This is a hint'); 45 | expect(el.getElementsByTagName('label')[0].textContent).toBe('This is a hint'); 46 | }); 47 | 48 | }); -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/script/iconFont.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | function getContent(className, str){ 4 | var regex = new RegExp('\.int-icon-' + className + '[\\s\\S]*?(".*")[\\s\\S]*?}'); 5 | var m; 6 | if ((m = regex.exec(str)) !== null) { 7 | return m[1]; 8 | } 9 | console.log(regex); 10 | } 11 | 12 | var fontFile = fs.readFileSync('../interactor-font-icons/fonts/interactor-icons.ttf').toString('base64'); 13 | var cssFile = fs.readFileSync('../interactor-font-icons/style.css').toString(); 14 | var templateFile = fs.readFileSync('./template.css').toString(); 15 | 16 | var newFile = templateFile.replace(/FILE/, fontFile); 17 | 18 | 19 | const classReg = /(\.int-icon-[\s\S]*?})/g; 20 | let m; 21 | let strings = []; 22 | while ((m = classReg.exec(cssFile)) !== null) { 23 | if (m.index === classReg.lastIndex) { 24 | classReg.lastIndex++; 25 | } 26 | 27 | if(m){ 28 | strings.push(m[1]); 29 | } 30 | } 31 | 32 | newFile = newFile.replace(/STYLES/, strings.join('\n')); 33 | 34 | 35 | //const radioReg = /\.int-icon-radiobox[\s\S]*?(".*")[\s\S]*?}/; 36 | 37 | newFile = newFile.replace(/RADIO_CONTENT/, getContent('radiobox', cssFile)); 38 | newFile = newFile.replace(/RADIO_CHECKED_CONTENT/, getContent('radiobox-marked', cssFile)); 39 | newFile = newFile.replace(/CHECKBOX_CONTENT/, getContent('checkbox-o', cssFile)); 40 | newFile = newFile.replace(/CHECKBOX_CHECKED_CONTENT/, getContent('checkbox', cssFile)); 41 | 42 | fs.writeFileSync('../../interactor-icons.css', newFile); 43 | 44 | //console.log(newFile); -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/service/mtosState/mtosState.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | 4 | export interface IHeader { 5 | PPQ?: number; 6 | bpm?: number; 7 | timeSignature?: number[]; 8 | } 9 | 10 | export interface INote { 11 | name?: string; 12 | midi?: number; 13 | time?: number; 14 | velocity?: number; 15 | duration?: number; 16 | } 17 | 18 | export interface ITrack { 19 | duration?: number; 20 | name?: string; 21 | notes?: INote[]; 22 | controlChanges?: any; 23 | instrumentNumber?: number; 24 | } 25 | 26 | 27 | @Injectable() 28 | export class MtosStateService { 29 | 30 | public header: IHeader; 31 | public tracks: ITrack[]; 32 | public selectedTrack: ITrack; 33 | public selectedNote: INote; 34 | 35 | constructor() { } 36 | 37 | setHeader(header: IHeader){ 38 | this.header = header; 39 | } 40 | 41 | setTracks(tracks: ITrack[]){ 42 | this.tracks = tracks; 43 | } 44 | 45 | selectTrack(index: number){ 46 | if(index < 0 || index >= this.tracks.length){ 47 | console.log('selectTrack: invalid track index.'); 48 | return; 49 | } 50 | 51 | this.selectedTrack = this.tracks[index]; 52 | this.selectedNote = false; 53 | } 54 | 55 | selectNote(index: number){ 56 | if(index < 0 || index >= this.selectedTrack.notes.length){ 57 | console.log('selectNote: invalid note index.'); 58 | return; 59 | } 60 | 61 | this.selectedNote = this.selectedTrack.notes[index]; 62 | } 63 | } -------------------------------------------------------------------------------- /experimental/sandbox/inc/scalesandnotes.js: -------------------------------------------------------------------------------- 1 | 2 | var noteLetters = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 3 | 4 | var scaleModes = { 5 | major: { 6 | ionian: [2,2,1,2,2,2,1], // last 1 added for 'wrap around' 7 | dorian: [2,1,2,2,2,1,2], 8 | phyrigian: [1,2,2,2,1,2,2], 9 | lydian: [2,2,2,1,2,2,1], 10 | mixolydian: [2,2,1,2,2,1,2], 11 | aeolian: [2,1,2,2,1,2,2], 12 | locrian: [1,2,2,1,2,2,2] 13 | }, 14 | harmonic_minor: { 15 | harmminor: [2,1,2,2,1,3,1], 16 | locriansharpsix:[1,2,2,1,3,1,2], 17 | ionianaug: [2,2,1,3,1,2,1], 18 | romanian: [2,1,3,1,2,1,2], 19 | phrygiandom: [1,3,1,2,1,2,2], 20 | lydiansharptwo: [3,1,2,1,2,2,1], 21 | ultralocrian: [1,2,1,2,2,1,3] 22 | }, 23 | melodic_minor: { 24 | jazz_minor: [2,1,2,2,2,2,1], 25 | dorianflatnine: [1,2,2,2,2,1,2], 26 | lydianaug: [2,2,2,2,1,2,1], 27 | lydiandom: [2,2,2,1,2,1,2], 28 | mixolydianflatsix: [2,2,1,2,1,2,2], 29 | semilocrian: [2,1,2,1,2,2,2], 30 | superlocrian: [1,2,1,2,2,2,2] 31 | } 32 | }; 33 | 34 | 35 | 36 | function getOctaveFromNoteNumber(noteNumber){ 37 | //return Math.floor(noteNumber/12) - 1; // Possibly -1 depending on which 'standard' you work with 38 | return Math.floor(noteNumber/12); 39 | } 40 | 41 | 42 | function noteNumberToNoteName(noteNumber, showOctave){ 43 | 44 | var noteName = noteLetters[noteNumber % 12]; 45 | if(showOctave){ 46 | noteName += getOctaveFromNoteNumber(noteNumber); 47 | } 48 | 49 | return noteName; 50 | } 51 | -------------------------------------------------------------------------------- /experimental/sandbox/inc/scales.js: -------------------------------------------------------------------------------- 1 | var noteLetters = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"] 2 | var notes = []; 3 | 4 | for (var num = 0; num < 128; num++) { 5 | notes.push({letter: noteLetters[num%12], octave: Math.floor(num/12)}); 6 | } 7 | 8 | var scaleModes = { 9 | major: { 10 | ionian: [2,2,1,2,2,2], 11 | dorian: [2,1,2,2,2,1], 12 | phyrigian: [1,2,2,2,1,2], 13 | lydian: [2,2,2,1,2,2], 14 | mixolydian: [2,2,1,2,2,1], 15 | aeolian: [2,1,2,2,1,2], 16 | locrian: [1,2,2,1,2,2] 17 | }, 18 | harmonic_minor: { 19 | harmminor: [2,1,2,2,1,3], 20 | locriansharpsix:[1,2,2,1,3,1], 21 | ionianaug: [2,2,1,3,1,2], 22 | romanian: [2,1,3,1,2,1], 23 | phrygiandom: [1,3,1,2,1,2], 24 | lydiansharptwo: [3,1,2,1,2,2], 25 | ultralocrian: [1,2,1,2,2,1] 26 | }, 27 | melodic_minor: { 28 | jazz_minor: [2,1,2,2,2,2], 29 | dorianflatnine: [1,2,2,2,2,1], 30 | lydianaug: [2,2,2,2,1,2], 31 | lydiandom: [2,2,2,1,2,1], 32 | mixolydianflatsix: [2,2,1,2,1,2], 33 | semilocrian: [2,1,2,1,2,2], 34 | superlocrian: [1,2,1,2,2,2] 35 | } 36 | }; 37 | 38 | function generateScale(root, scale, mode) { 39 | var result = []; 40 | var gaps = scaleModes[scale][mode]; 41 | if (gaps === undefined) return undefined; 42 | result.push(root); 43 | var lastNoteNum = noteLetters.indexOf(root); 44 | for (var i=0; ivoid){ 36 | var dialog: IDialog = { 37 | heading: heading, 38 | content: content, 39 | select: [select], 40 | level: 'info', 41 | buttons: [{ text: 'Ok', callback: callback }] 42 | } 43 | 44 | return this.openDialog(dialog); 45 | } 46 | 47 | public openQuestion(heading: string, content: string, confirm: ()=>void, decline: ()=>void){ 48 | var dialog: IDialog = { 49 | heading: heading, 50 | content: content, 51 | level: 'info', 52 | buttons: [ 53 | { text: 'Yes', callback: confirm }, 54 | { text: 'No', callback: decline } 55 | ] 56 | } 57 | 58 | return this.openDialog(dialog); 59 | } 60 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/persons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/README.md: -------------------------------------------------------------------------------- 1 | # Warning 2 | 3 | **This project environment is setup to work on windows. I can not guarantee it will work in other environments**. 4 | 5 | # Prerequisites 6 | 7 | You will need [Node js](https://nodejs.org/en/) or node package manager. 8 | 9 | IDE that supports [Typescript](https://www.typescriptlang.org/). I recommend [Visual studio code](https://code.visualstudio.com/) 10 | 11 | This project uses Angular 2 as a framwork. If you do not know it here are some places to start. 12 | 13 | [angular.io](https://angular.io/) and 14 | [egghead.io](https://egghead.io/courses/get-started-with-angular-2) 15 | 16 | # Getting started 17 | 18 | Open CMD in the same folder as package.js. 19 | 20 | Type and run `npm install` 21 | 22 | This will download all dependencies. 23 | 24 | Type and run `npm run build:dev` 25 | 26 | This will build the project to the dist folder and watch the project files. 27 | First run will takes some time but the next after that will be way faster. 28 | If you make changes to the files it will automatically rebuild the project. 29 | 30 | You will get some typing errors. Do not mind those. They have no effect on the acctual code. 31 | 32 | Open a new CMD and type and run `npm run server` 33 | 34 | This will server the file from a small server and it should open a browser on the correct page. 35 | 36 | Happy coding. 37 | 38 | # Build new version 39 | 40 | Type and run `npm run build:prod` 41 | 42 | Open a new CMD and type and run `npm run server` 43 | 44 | Test application. 45 | 46 | Copy the content in build folder. 47 | 48 | Remove app.[hash].js and polyfills.[hash].js from dist folder. 49 | 50 | Paste clipboard to dist folder. Overwrite every file. 51 | 52 | Commit, push and check if your changes appears on the website. -------------------------------------------------------------------------------- /experimental/musicboxeditor/karma.config.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require("./webpack.config.js"); 2 | 3 | var testEnvironment = process.env.TEST_ENVIRONMENT || 'prod'; 4 | 5 | console.log('Testing with test environment: ', testEnvironment); 6 | 7 | var karmaConfig = { 8 | basePath: '', 9 | 10 | frameworks: ['jasmine'], 11 | 12 | exclude:[], 13 | 14 | files: [ 15 | {pattern: './src/main.test.ts', watched: false}, 16 | {pattern: './src/styles.css', watched: false}, 17 | {pattern: './src/assets/icons/interactor-icons.css', watched: false} 18 | ], 19 | 20 | preprocessors: { 21 | './src/main.test.ts':["webpack", 'sourcemap'] 22 | }, 23 | webpack: webpackConfig, 24 | 25 | webpackServer: {noInfo: true}, 26 | 27 | port: 9876, 28 | 29 | colors: true, 30 | 31 | reporters: ["spec"], 32 | 33 | specReporter: { 34 | maxLogLines: 5, // limit number of lines logged per test 35 | suppressErrorSummary: true, // do not print error summary 36 | suppressFailed: false, // do not print information about failed tests 37 | suppressPassed: false, // do not print information about passed tests 38 | suppressSkipped: true, // do not print information about skipped tests 39 | showSpecTiming: true // print the time elapsed for each spec 40 | }, 41 | 42 | browsers: ['Chrome'], 43 | 44 | mime: { 45 | 'text/x-typescript': ['ts','tsx'] 46 | }, 47 | 48 | concurrency: Infinity, 49 | 50 | autoWatch: false, 51 | 52 | singleRun: true, 53 | }; 54 | 55 | if(testEnvironment == 'dev'){ 56 | karmaConfig.autoWatch = true; 57 | karmaConfig.singleRun = false; 58 | } 59 | 60 | module.exports = function (config) { 61 | config.set(karmaConfig); 62 | }; 63 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/script/template.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'interactor-icons'; 3 | src: url(data:application/octet-stream;charset=utf-8;base64,FILE) format('truetype'); 4 | } 5 | 6 | [class^="int-icon-"], [class*=" int-icon-"] { 7 | /* use !important to prevent issues with browser extensions that change fonts */ 8 | font-family: 'interactor-icons' !important; 9 | speak: none; 10 | font-style: normal; 11 | font-weight: normal; 12 | font-variant: normal; 13 | text-transform: none; 14 | line-height: 1em; 15 | /* Better Font Rendering =========== */ 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | } 19 | 20 | .icon-click { 21 | padding: 0.2em; 22 | } 23 | 24 | .icon-click:hover { 25 | background-color: rgba(0, 0, 0, 0.10); 26 | } 27 | 28 | .icon-click:active { 29 | background-color: rgba(0, 0, 0, 0.2); 30 | } 31 | 32 | input[type=radio], input[type=checkbox] { 33 | font-family: 'interactor-icons' !important; 34 | font-size: 1em; 35 | font-style: normal; 36 | font-weight: normal; 37 | font-variant: normal; 38 | text-transform: none; 39 | line-height: 1em; 40 | /* Better Font Rendering =========== */ 41 | -webkit-font-smoothing: antialiased; 42 | -moz-osx-font-smoothing: grayscale; 43 | 44 | padding: 0; 45 | margin: 0; 46 | border: none; 47 | box-shadow: none; 48 | background: transparent; 49 | -webkit-appearance: none; 50 | } 51 | 52 | input[type=radio]::before { 53 | content: RADIO_CONTENT; 54 | } 55 | 56 | input[type=radio]:checked::before { 57 | content: RADIO_CHECKED_CONTENT; 58 | } 59 | 60 | input[type=checkbox]::before { 61 | content: CHECKBOX_CONTENT; 62 | } 63 | 64 | input[type=checkbox]:checked::before { 65 | content: CHECKBOX_CHECKED_CONTENT; 66 | } 67 | 68 | STYLES -------------------------------------------------------------------------------- /dev/midi_libraries/MidiConvert.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MidiConvert.js Object Viewer 6 | 7 | 8 | 9 | 10 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | Homemidi-parser.jsMidiConvert.js 48 |
49 |
50 |
51 |

MidiConvert.js object viewer

52 | 53 |

Load a MIDI file. A JSON reprsentation of the parsed MIDI will be displayed below.

54 | MidiConvert GitHub 55 |
56 |
57 | 58 |
59 |
60 | 61 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenuRenderer.component.ts: -------------------------------------------------------------------------------- 1 | //our root app component 2 | import { Component, ViewChild, ElementRef, OnInit, Input } from '@angular/core'; 3 | import { ContextMenuService } from "./contextMenu.service"; 4 | import { IContextMenuOptions } from "./contextMenu.interface"; 5 | 6 | @Component({ 7 | selector: 'context-menu-renderer', 8 | templateUrl: './contextMenuRenderer.component.html', 9 | styleUrls: ['./contextMenuRenderer.component.css'] 10 | }) 11 | export class ContextMenuRendererComponent implements OnInit { 12 | 13 | @Input() options: IContextMenuOptions 14 | 15 | constructor(private elementRef: ElementRef, private contextMenuService: ContextMenuService) { 16 | 17 | } 18 | 19 | ngOnInit(): void { 20 | this.contextMenuService.setRenderer(this); 21 | } 22 | 23 | //logic 24 | open(options: IContextMenuOptions) { 25 | if (!options) { 26 | return; 27 | } 28 | this.options = options; 29 | setTimeout(()=>{ 30 | this.elementRef.nativeElement.children[0].focus(); 31 | }, 0); 32 | } 33 | 34 | close() { 35 | this.options = null; 36 | } 37 | 38 | getEncapsulationId(): string { 39 | if (!this.elementRef) { 40 | return ''; 41 | } 42 | 43 | var attr = this.elementRef.nativeElement.attributes; 44 | for(let i = 0; i < attr.length; i++){ 45 | let s:string = attr[i].name; 46 | if(s.includes('_nghost')){ 47 | return '_ngcontent' + s.substring(7); 48 | } 49 | } 50 | 51 | return ''; 52 | } 53 | 54 | //template 55 | showContextMenu(): boolean { 56 | if (!this.options) { 57 | return false; 58 | } 59 | 60 | return true; 61 | } 62 | 63 | onBlurContextMenu(){ 64 | this.close(); 65 | } 66 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .app { 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | right: 0; 6 | left: 0; 7 | padding: 0.5em; 8 | color: #444444; 9 | background-color: #f8f8f8; 10 | } 11 | 12 | .app>*+*{ 13 | margin-top: 0.5em; 14 | } 15 | 16 | .header { 17 | display: flex; 18 | justify-content: space-between; 19 | color: #fff; 20 | background-color: #444; 21 | } 22 | 23 | .header>h1{ 24 | padding: 0.25em; 25 | } 26 | 27 | sub-menu{ 28 | color: white; 29 | } 30 | 31 | .main-view { 32 | display: flex; 33 | flex-direction: column; 34 | flex: 100 100 auto; 35 | min-height: 0; 36 | border: 1px solid #444444; 37 | background-color: white; 38 | } 39 | 40 | .menu-view{ 41 | position: relative; 42 | display: flex; 43 | flex-wrap: wrap; 44 | align-items:stretch; 45 | } 46 | 47 | .cell{ 48 | position: relative; 49 | flex: 100 100 33.3%; 50 | padding-bottom: 1em; 51 | } 52 | 53 | .sub-cell{ 54 | overflow: auto; 55 | white-space:nowrap 56 | } 57 | 58 | .sub-cell>*{ 59 | display: inline-block; 60 | } 61 | 62 | .menu-view>label>input, 63 | .menu-view>label>select{ 64 | width: 200px; 65 | } 66 | 67 | 68 | .track-view{ 69 | display: flex; 70 | border: 1px #666 solid; 71 | background-color: #fff; 72 | } 73 | 74 | .notes{ 75 | border-right: 1px #666 solid; 76 | } 77 | 78 | text.note { 79 | cursor: pointer; 80 | } 81 | 82 | text.note:hover { 83 | fill: cornflowerblue; 84 | } 85 | 86 | .track{ 87 | flex: 100 100 50%; 88 | min-width: 0; 89 | overflow-x: auto; 90 | } 91 | 92 | /* 93 | .main-view-column { 94 | position: relative; 95 | flex: 100 100 50%; 96 | min-width: 0; 97 | padding: 0.5em; 98 | overflow-x: hidden; 99 | overflow-y: auto; 100 | } 101 | 102 | .main-view-column+.main-view-column { 103 | border-left: 1px solid #444444; 104 | } 105 | */ 106 | -------------------------------------------------------------------------------- /dev/midi_libraries/midi_parser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | midi-parser.js Object Viewer 6 | 7 | 8 | 9 | 10 | 42 | 43 | 44 | 45 | 46 |
47 | Homemidi-parser.jsMidiConvert.js 48 |
49 |
50 | 51 |
52 |

midi-parser.js object viewer

53 | 54 |

Load a MIDI file. A JSON reprsentation of the parsed MIDI will be displayed below.

55 | mid-parser.js GitHub 56 | 57 |
58 |
59 | 60 |
61 |
62 | 63 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/hint/hint.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild, ElementRef} from '@angular/core' 2 | 3 | import {HintService} from './hint.service'; 4 | 5 | @Component({ 6 | selector: 'hint-renderer', 7 | providers: [], 8 | templateUrl: './hint.component.html', 9 | styleUrls: ['./hint.component.css'], 10 | }) 11 | export class HintComponent { 12 | private offset = 3; 13 | 14 | 15 | @ViewChild('box') public hintBox: ElementRef; 16 | 17 | public hint: string; 18 | public pos: { x: number, y: number }; 19 | public bbox: ClientRect; 20 | 21 | 22 | constructor( private hintService: HintService) { 23 | this.hintService.setRenderer(this); 24 | this.pos = {x: 0, y: 0}; 25 | } 26 | 27 | 28 | public close() { 29 | this.hint = null; 30 | } 31 | 32 | public open(hint: string, bbox: ClientRect) { 33 | this.hint = hint; 34 | this.bbox = this.getBbox(); 35 | 36 | var left = bbox.left; 37 | if(left + this.bbox.width > window.innerWidth){ 38 | left = bbox.right - this.bbox.width; 39 | } 40 | 41 | if(left < 0){ 42 | left = 0 + this.offset; 43 | } 44 | 45 | var top = bbox.bottom + this.offset; 46 | if(top + this.bbox.height > window.innerHeight){ 47 | top = bbox.top - this.bbox.height - this.offset; 48 | } 49 | 50 | if(top < 0){ 51 | top = 0 + this.offset; 52 | } 53 | 54 | this.pos.x = left; 55 | this.pos.y = top; 56 | } 57 | 58 | public getBbox(): ClientRect { 59 | var test = this.getVirtualBox(); 60 | document.body.appendChild(test); 61 | var bbox = test.children[0].getBoundingClientRect(); 62 | document.body.removeChild(test); 63 | return bbox; 64 | } 65 | 66 | public getVirtualBox(): HTMLElement { 67 | var tag = document.createElement("hint-renderer"); 68 | var box = document.createElement("div"); 69 | var text = document.createElement("label"); 70 | box.classList.add('box'); 71 | text.classList.add('text'); 72 | text.innerText = this.hint; 73 | box.appendChild(text); 74 | tag.appendChild(box); 75 | 76 | return tag; 77 | } 78 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/tabMenu/tabMenu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { ITabMenuComponentOptions, ITabMenuButton } from './tabMenu.interface'; 3 | 4 | @Component({ 5 | selector: 'tab-menu', 6 | templateUrl: './tabMenu.component.html', 7 | styleUrls: ['./tabMenu.component.css'], 8 | }) 9 | export class TabMenuComponent implements OnInit { 10 | 11 | 12 | @Input() options: ITabMenuComponentOptions; 13 | 14 | public selected: ITabMenuButton = null; 15 | 16 | constructor() { } 17 | 18 | ngOnInit(): void { 19 | if(this.options){ 20 | this.options.component = this; 21 | if(this.options.onInit){ 22 | this.options.onInit(); 23 | } 24 | } 25 | } 26 | 27 | selectButton(indexOrButton: number | ITabMenuButton){ 28 | let button: ITabMenuButton; 29 | 30 | if(typeof indexOrButton === 'number'){ 31 | if(indexOrButton < 0 || indexOrButton >= this.options.buttons.length){ 32 | return; 33 | } 34 | button = this.options.buttons[indexOrButton]; 35 | }else{ 36 | button = indexOrButton; 37 | } 38 | 39 | if(!button){ return; } 40 | 41 | if(button.callback && button.callback() === true){ 42 | this.selected = button; 43 | } 44 | } 45 | 46 | onClickButton(button: ITabMenuButton) { 47 | this.selectButton(button); 48 | } 49 | 50 | showTabMenu():boolean { 51 | if(!this.options){ 52 | return false; 53 | } 54 | return true; 55 | } 56 | 57 | getButtonClass(button: ITabMenuButton): Object { 58 | if(!button){ return {}; } 59 | return { 60 | isSelected: this.isSelectedButton(button) 61 | } 62 | } 63 | 64 | isSelectedButton(button: ITabMenuButton) { 65 | if(!button){ return false; } 66 | if(this.selected === button){ 67 | return true; 68 | } 69 | return false; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /experimental/sandbox/templates/strip_dimensions.xml: -------------------------------------------------------------------------------- 1 | 7VvLcqM4FP0aV80skkISYLOMk56ZTVenKovpXspGtlUNyANyYufrR4AEekDs2DhxJ+1N4CJdxD33oSMpI3Sbbv/O8Xr1lcUkGUEv3o7Q3QhCOAk98aeU7GoJ8KGULHMaS1kreKDPRApVsw2NSWE05IwlnK5N4ZxlGZlzQ4bznD2ZzRYsMd+6xkviCB7mOHGl/9KYr6Q0DGD74B9Clyv1ahBG9ZMZnv9c5myTyReOIFpUv/pxipUy+aXFCsfsSROhLyN0mzPG66t0e0uS0rrKbnW/v3qeNgPPScYP6QDHdY9HnGyIGnI1ML5T1iBZfFMaVdzNE1wUdD5C04LjnLviFU8TIQDiMsbFisTazT3mnORZJYEerJTk7Ce5ZQnLq1chr/qJJ/UgSOzg1H4YaMwlHJGwlPB8J5o8tYAF0sgrDSoly0mCOX001WPpN8tGXfOGe0bFi6G3Vb4g9SgX90JTRcE2+ZzIXjoAtiK0T5Mw9JJwR5O40L67FVUA94A9ccBO8Zamm7T2cht4TrbcxDQnBX3Gs6pBCdO6HFU1zmA6Cu6EBCd0WUI8FzgRger0keScitC6kQ9SGsdl/2mCZySZNvGieYGMGOUFpQayNYCSgS9H0oaT7h+1a7sOIjVdedeeB8am7U/zDdWELRYFORUsf39gPq0oJw9rPC/vn0QuNsFyoisMZXQtaJIoecYyYkdrr90PiL++APGlW2vxCaDXEaFIgdllccOgL7n6Ga03gHWgb1ln0mEdz++wTtPzFOugDuuESRnrC1Z5cmum8L8NUw+uiqpS35TZ219v24fialn+feA5XYundzQlWUFZVii9YkS16rrhAanGtL/00g7HPTzhdAE+AJYWlOpWRzLqAHIIHAMHR87WX0W9oNnFWHjoxDJWX61CJ3BDZ9wVOAPYO3TsPWOcs/STmdxHb2dyd36akAX/4AYHYzOnwOjtDB45Bs/LF3x0i8PJ+5kcuHncMbRGwrpmbC9NXBpSJWyR774LoXcNgK8EPyqB502U4J7kon5XoFSTfLKlXPUK5H3ZCYhOvry3+4wO5G/iGyuiZMzaasajid6L5dncDI0tFYeyPFsR9C1Fw5E84BapKoDvcRzTbPlxIxi8ZwS7WfOzRPC7rcCAEJizEjVLefUSjMNVJ+dbgvE65zMfPTjhO05o1GgHZ7vbSye3g6MIrTjpoF6daxZDoNi1ojMEirtPhyKwUBy7KMJzoXjAqqZbJ4faVGjKoFYCq2Vg1FMDtXIb6MW2RWKIGe67llFkr0QqiF49xY1MRWNLz4A19Ci6NLQTAc2JNPf4rrvOD/1mIIp0aRyp0aMcKAqukf47kjI5es84KXNn73EJVEoz4SbQm5XWTPCObcrxCY+o9i28RXWFs9bWF1kyyqL2IMcE5b2xrVH+hiktoYWZYmR6ZTlXaUHuzPqlrPBmO9l95m6yRUnCgFFdrj2A9lM52Uujcr1lLJlVH7diOX0Ww8HJh1mhsbNEcOw+/NgqXwE8LN0Ix8E7rZncEe8fsG+y1kDui/amQXtce9qPw9e1B3Dy4oDERf2JxyZX5NKvotwmvNAjB1a4NmG876jKkYcUkDfqjB7tkAKKoAHQFRoocjq1DnqGAbmkbUG3VU2taqdbZXku0CmrLcmWvwuqU1BRxzmB81XUrmMCl1dRf5kzY+5uwrFnxuwFS0fTcHNj5DL2ImG8+MXS92lJGu1N0mYNvYKnOcywSbjzlMg3qVp87lStgM1ytfhVbNI/SpjrVn/2NatPP3xrR/mZczXwrakanPhOsgagI7/4Qb9nHJys3W2/i07W2tJaFEUjayer2drqpz9RNNHpTw/1ObQmXDbLAb6d8NGxpSMAtpOeh+cA3z4XB/cQEbsDmIytADiRiLhHiSoiIg/yf5JSFr6Yw0Ts+SEya9kwdOPK1AqgqWCYSueebzc2V+si5unzFym6/BMSb0k6rM3a4IykQ9y2/+xSQ97+TxH68j8= -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/dialog/dialog.component.css: -------------------------------------------------------------------------------- 1 | .dialog-renderer { 2 | font-family: sans-serif; 3 | 4 | display: block; 5 | position: absolute; 6 | top: 0; 7 | bottom: 0; 8 | right: 0; 9 | left: 0; 10 | } 11 | 12 | .background{ 13 | position: absolute; 14 | width: 100%; 15 | height: 100%; 16 | background-color: transparent; 17 | background-color: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | .dialog-wrapper{ 21 | display: flex; 22 | flex-direction: column; 23 | justify-content: center; 24 | position: absolute; 25 | /* 26 | left: 0; 27 | right: 0; 28 | bottom: 30%; 29 | transform: translate(0, 30%); 30 | */ 31 | left: 50%; 32 | top: 50%; 33 | transform: translate(-50%, -50%); 34 | font-size: 14px; 35 | color: #696969; 36 | background: #F8F8F8; 37 | box-shadow: rgba(0, 0, 0, 0.2) 4px 4px 0 0; 38 | } 39 | 40 | .content{ 41 | white-space: pre; 42 | } 43 | 44 | .dialog{ 45 | position: relative; 46 | padding: 1em; 47 | /*flex: 100 100 50%;*/ 48 | } 49 | 50 | .heading{ 51 | font-size: 1.2em; 52 | } 53 | 54 | *+.content{ 55 | margin-top: 0.5em; 56 | } 57 | 58 | *+.select{ 59 | margin-top: 0.5em; 60 | } 61 | 62 | *+.buttons{ 63 | margin-top: 0.5em; 64 | } 65 | 66 | .buttons{ 67 | display: flex; 68 | justify-content: flex-end; 69 | } 70 | 71 | .buttons button+button{ 72 | margin-left: 0.5em; 73 | } 74 | 75 | .buttons button{ 76 | border: none; 77 | padding: 0.2em 0.5em; 78 | color: inherit; 79 | background-color: rgba(0, 0, 0, 0.2); 80 | } 81 | 82 | .buttons button:hover{ 83 | color: inherit; 84 | background-color: rgba(0, 0, 0, 0.1); 85 | } 86 | 87 | .buttons button:active{ 88 | color: inherit; 89 | background-color: rgba(0, 0, 0, 0.4); 90 | } 91 | 92 | .success{ 93 | color: white; 94 | background: #346A3B; 95 | } 96 | 97 | .info{ 98 | /* 99 | color: white; 100 | background: #35496A; 101 | */ 102 | background: white; 103 | } 104 | 105 | .warning{ 106 | color: white; 107 | background: #D69600; 108 | } 109 | 110 | .error{ 111 | color: white; 112 | background: #980707; 113 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/dist/styles.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: app-font; 3 | src: url(./assets/fonts/opensans.woff2); 4 | } 5 | 6 | html { 7 | font-family: app-font; 8 | speak: none; 9 | font-style: normal; 10 | font-weight: normal; 11 | font-variant: normal; 12 | text-transform: none; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | font-size: 12px; 16 | } 17 | 18 | h1 { 19 | margin: 0; 20 | font-size: 1.5em; 21 | } 22 | 23 | h2 { 24 | margin: 0; 25 | font-size: 1.25em; 26 | } 27 | 28 | h3 { 29 | margin: 0; 30 | font-size: 1.15em; 31 | } 32 | 33 | h4 { 34 | margin: 0; 35 | font-size: 1.1em; 36 | } 37 | 38 | h5 { 39 | margin: 0; 40 | font-size: 1.05em; 41 | } 42 | 43 | p { 44 | margin: 0; 45 | } 46 | 47 | *:focus { 48 | outline-offset: 1px; 49 | outline: 2px solid #6495ED; 50 | } 51 | 52 | button { 53 | font-family: app-font; 54 | border: none; 55 | padding: 0.2em 0.5em; 56 | background-color: rgba(0, 0, 0, 0.15); 57 | } 58 | 59 | button:hover { 60 | background-color: rgba(0, 0, 0, 0.10); 61 | } 62 | 63 | button:active { 64 | background-color: rgba(0, 0, 0, 0.2); 65 | } 66 | 67 | input[type=text], 68 | input[type=number], 69 | input[type=time], 70 | input[type=date], 71 | input[type=datetime], 72 | input[type=datetime-local], 73 | input[type=week], 74 | input[type=month], 75 | select { 76 | padding: 0.2em 0.2em; 77 | margin: 0; 78 | align-items: center; 79 | border: 1px solid #bbbbbb; 80 | box-shadow: inset 0px 0px 3px 0px rgba(0, 0, 0, 0.20); 81 | } 82 | 83 | 84 | /*hint component hack - can be removed when we get viewencaptulation.none working*/ 85 | hint-renderer{ 86 | font-family: app-font; 87 | pointer-events: none; 88 | position: absolute; 89 | top: 0; 90 | left: 0; 91 | width: 0; 92 | height: 0; 93 | overflow: hidden; 94 | } 95 | 96 | hint-renderer .box { 97 | position: fixed; 98 | padding: 0.5em; 99 | max-width: 20em; 100 | color: #444444; 101 | background-color: white; 102 | border: 1px solid #e0e0e0; 103 | box-shadow: rgba(0, 0, 0, 0.2) 4px 4px 0 0; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/styles.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: app-font; 3 | src: url(./assets/fonts/opensans.woff2); 4 | } 5 | 6 | html { 7 | font-family: app-font; 8 | speak: none; 9 | font-style: normal; 10 | font-weight: normal; 11 | font-variant: normal; 12 | text-transform: none; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | font-size: 12px; 16 | } 17 | 18 | h1 { 19 | margin: 0; 20 | font-size: 1.5em; 21 | } 22 | 23 | h2 { 24 | margin: 0; 25 | font-size: 1.25em; 26 | } 27 | 28 | h3 { 29 | margin: 0; 30 | font-size: 1.15em; 31 | } 32 | 33 | h4 { 34 | margin: 0; 35 | font-size: 1.1em; 36 | } 37 | 38 | h5 { 39 | margin: 0; 40 | font-size: 1.05em; 41 | } 42 | 43 | p { 44 | margin: 0; 45 | } 46 | 47 | *:focus { 48 | outline-offset: 1px; 49 | outline: 2px solid #6495ED; 50 | } 51 | 52 | button { 53 | font-family: app-font; 54 | border: none; 55 | padding: 0.2em 0.5em; 56 | background-color: rgba(0, 0, 0, 0.15); 57 | } 58 | 59 | button:hover { 60 | background-color: rgba(0, 0, 0, 0.10); 61 | } 62 | 63 | button:active { 64 | background-color: rgba(0, 0, 0, 0.2); 65 | } 66 | 67 | input[type=text], 68 | input[type=number], 69 | input[type=time], 70 | input[type=date], 71 | input[type=datetime], 72 | input[type=datetime-local], 73 | input[type=week], 74 | input[type=month], 75 | select { 76 | padding: 0.2em 0.2em; 77 | margin: 0; 78 | align-items: center; 79 | border: 1px solid #bbbbbb; 80 | box-shadow: inset 0px 0px 3px 0px rgba(0, 0, 0, 0.20); 81 | } 82 | 83 | 84 | /*hint component hack - can be removed when we get viewencaptulation.none working*/ 85 | hint-renderer{ 86 | font-family: app-font; 87 | pointer-events: none; 88 | position: absolute; 89 | top: 0; 90 | left: 0; 91 | width: 0; 92 | height: 0; 93 | overflow: hidden; 94 | } 95 | 96 | hint-renderer .box { 97 | position: fixed; 98 | padding: 0.5em; 99 | max-width: 20em; 100 | color: #444444; 101 | background-color: white; 102 | border: 1px solid #e0e0e0; 103 | box-shadow: rgba(0, 0, 0, 0.2) 4px 4px 0 0; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /experimental/whitebird/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,a=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},i=/constructor/i.test(e.HTMLElement)||e.safari,f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",d=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,d)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(a){u(a)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,d){if(!d){t=p(t)}var v=this,w=t.type,m=w===s,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&i)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;a(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define("FileSaver.js",function(){return saveAs})} 3 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenu.component.ts: -------------------------------------------------------------------------------- 1 | //our root app component 2 | import { Component, ViewChild, ElementRef, OnInit, Input, HostBinding } from '@angular/core'; 3 | import { ContextMenuService } from "./contextMenu.service"; 4 | import { IContextMenuOptions } from "./contextMenu.interface"; 5 | 6 | @Component({ 7 | selector: 'context-menu', 8 | templateUrl: './contextMenu.component.html', 9 | styleUrls: ['./contextMenu.component.css'] 10 | }) 11 | export class ContextMenuComponent implements OnInit { 12 | 13 | @Input() options: IContextMenuOptions; 14 | 15 | @HostBinding('style.left.px') x = 0; 16 | @HostBinding('style.top.px') y = 0; 17 | @HostBinding('style.width.px') width = 0; 18 | @HostBinding('style.height.px') height = 0; 19 | 20 | private openElement: IContextMenuOptions; 21 | 22 | constructor(private elementRef: ElementRef, private contextMenuService: ContextMenuService) { 23 | } 24 | 25 | ngOnInit(): void { 26 | if(!this.options){ 27 | return; 28 | } 29 | 30 | this.x = this.options.x ? this.options.x : 0; 31 | this.y = this.options.y ? this.options.y : 0; 32 | this.width = this.options.width ? this.options.width : 50; 33 | this.height = this.options.height ? this.options.height : 50; 34 | } 35 | 36 | //template 37 | showSubMenu(menu: IContextMenuOptions): boolean { 38 | if(this.openElement === menu && this.openElement.menu && this.openElement.menu.length > 0){ 39 | return true; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | showSubMenuIcon(menu: IContextMenuOptions): boolean{ 46 | if(!menu || !menu.menu || menu.menu.length <= 0){ 47 | return false; 48 | } 49 | 50 | return true; 51 | } 52 | 53 | getIconClass(menu: IContextMenuOptions): string{ 54 | if(menu.icon){ 55 | return menu.icon; 56 | } 57 | return ''; 58 | } 59 | 60 | onBlurMenu() { 61 | this.contextMenuService.closeContextMenu(); 62 | } 63 | 64 | onClickMenu(menu: IContextMenuOptions) { 65 | if (!menu || !menu.callback) { 66 | return; 67 | } 68 | 69 | menu.callback(); 70 | 71 | this.contextMenuService.closeContextMenu(); 72 | } 73 | 74 | onMouseEnterElement(menu: IContextMenuOptions){ 75 | this.openElement = menu; 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "musicboxeditor", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "postinstall": "typings install", 7 | "build": "webpack --progress", 8 | "build:dev": "webpack --progress --watch", 9 | "build:prod": "rimraf build && cross-env APP_ENVIRONMENT=production webpack -p --progress", 10 | "server": "lite-server -c lite-server.config.js", 11 | "server:gzip": "cross-env APP_ENVIRONMENT=gzip lite-server -c lite-server.config.js", 12 | "test:dev": "cross-env APP_ENVIRONMENT=test TEST_ENVIRONMENT=dev ./node_modules/karma/bin/karma start ./karma.config.js", 13 | "test:prod": "cross-env APP_ENVIRONMENT=test ./node_modules/karma/bin/karma start ./karma.config.js" 14 | }, 15 | "dependencies": { 16 | "@angular/common": "2.2.3", 17 | "@angular/compiler": "2.2.3", 18 | "@angular/compiler-cli": "2.2.3", 19 | "@angular/core": "2.2.3", 20 | "@angular/forms": "2.2.3", 21 | "@angular/http": "2.2.3", 22 | "@angular/platform-browser": "2.2.3", 23 | "@angular/platform-browser-dynamic": "2.2.3", 24 | "@angular/platform-server": "2.2.3", 25 | "@angular/router": "3.2.3", 26 | "core-js": "2.4.1", 27 | "jszip": "^3.1.3", 28 | "midiconvert": "^0.4.1", 29 | "reflect-metadata": "^0.1.10", 30 | "rxjs": "5.0.0-beta.12", 31 | "zone.js": "0.6.26" 32 | }, 33 | "devDependencies": { 34 | "@ngtools/webpack": "1.1.9", 35 | "@types/angular": "^1.6.10", 36 | "@types/node": "^7.0.12", 37 | "@types/webpack-env": "^1.13.0", 38 | "angular2-template-loader": "0.6.0", 39 | "compression": "^1.6.2", 40 | "copy-webpack-plugin": "^4.0.1", 41 | "cross-env": "3.1.3", 42 | "enhanced-resolve": "3.0.0", 43 | "extract-text-webpack-plugin": "1.0.1", 44 | "html-webpack-plugin": "2.24.1", 45 | "http-proxy-middleware": "^0.17.4", 46 | "jasmine-core": "^2.5.2", 47 | "karma": "^1.5.0", 48 | "karma-chrome-launcher": "^2.0.0", 49 | "karma-jasmine": "^1.1.0", 50 | "karma-sourcemap-loader": "^0.3.7", 51 | "karma-spec-reporter": "0.0.30", 52 | "karma-webpack": "^2.0.3", 53 | "lite-server": "^2.3.0", 54 | "raw-loader": "0.5.1", 55 | "rimraf": "2.5.4", 56 | "ts-loader": "1.3.0", 57 | "typescript": "2.0.10", 58 | "typings": "2.0.0", 59 | "webpack": "2.1.0-beta.27", 60 | "webpack-dev-server": "2.1.0-beta.12", 61 | "webpack-visualizer-plugin": "^0.1.11" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /dev/UML/Application model.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/dialog/dialog.component.ts: -------------------------------------------------------------------------------- 1 | //our root app component 2 | import { Component, ViewChild, ElementRef } from '@angular/core'; 3 | import { DialogService } from './dialog.service'; 4 | import { IDialog, IDialogButton } from './dialog.interface'; 5 | 6 | @Component({ 7 | selector: 'dialog-renderer', 8 | templateUrl: './dialog.component.html', 9 | styleUrls: ['./dialog.component.css'] 10 | }) 11 | export class DialogComponent { 12 | 13 | @ViewChild('buttons') public buttons: ElementRef; 14 | dialog: IDialog; 15 | 16 | constructor(private dialogService: DialogService) { 17 | this.dialogService.setRenderer(this); 18 | this.dialog; 19 | } 20 | 21 | public openDialog(dialog: IDialog): void{ 22 | if(!dialog){ 23 | return; 24 | } 25 | 26 | 27 | this.dialog = dialog; 28 | 29 | console.log(this); 30 | setTimeout(()=>{ 31 | var buttonElements = this.buttons.nativeElement.getElementsByTagName('button'); 32 | if(buttonElements && buttonElements.length > 0){ 33 | buttonElements[0].focus(); 34 | } 35 | }, 0) 36 | } 37 | 38 | public closeDialog(): void{ 39 | this.dialog = null; 40 | } 41 | 42 | 43 | onBlurDialog(event: any){ 44 | console.log(event); 45 | } 46 | 47 | public getDialogClass(): string{ 48 | return this.dialog.level; 49 | } 50 | 51 | public showDialog(): boolean{ 52 | if(!this.dialog){ 53 | return false; 54 | } 55 | return true; 56 | } 57 | 58 | public showHeading(): boolean{ 59 | if(!this.dialog || !this.dialog.heading){ 60 | return false; 61 | } 62 | return true; 63 | } 64 | 65 | public showContent(): boolean{ 66 | if(!this.dialog || !this.dialog.content){ 67 | return false; 68 | } 69 | return true; 70 | } 71 | 72 | public showSelect(): boolean{ 73 | if(!this.dialog || !this.dialog.select){ 74 | return false; 75 | } 76 | return true; 77 | } 78 | 79 | public showButtons(): boolean{ 80 | if(!this.dialog || !this.dialog.buttons){ 81 | return false; 82 | } 83 | 84 | if(this.dialog.buttons.length <= 0){ 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | 91 | public onClickButton(button: IDialogButton): void{ 92 | if(!button){ 93 | return; 94 | } 95 | 96 | if(!button.disableClose){ 97 | this.closeDialog(); 98 | } 99 | 100 | if(button.callback){ 101 | button.callback(); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /experimental/sandbox/templates/strip_event_orientations: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/subMenu/subMenu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | import { ISubMenuComponentOptions, ISubMenuButton} from './subMenu.interface'; 4 | 5 | @Component({ 6 | selector: 'sub-menu', 7 | templateUrl: './subMenu.component.html', 8 | styleUrls: ['./subMenu.component.css'] 9 | }) 10 | export class SubMenuComponent implements OnInit { 11 | constructor() { } 12 | 13 | @Input() options: ISubMenuComponentOptions; 14 | 15 | ngOnInit(): void { 16 | if(this.options){ 17 | this.options.component = this; 18 | if(this.options.onInit){ 19 | this.options.onInit(); 20 | } 21 | } 22 | } 23 | 24 | selectButton(indexOrButton: number | ISubMenuButton){ 25 | let button: ISubMenuButton; 26 | 27 | if(typeof indexOrButton === 'number'){ 28 | if(indexOrButton < 0 || indexOrButton >= this.options.buttons.length){ 29 | return; 30 | } 31 | button = this.options.buttons[indexOrButton]; 32 | }else{ 33 | button = indexOrButton; 34 | } 35 | 36 | if(!button){ return; } 37 | 38 | if(button.callback){ 39 | button.callback(); 40 | } 41 | } 42 | 43 | onClickButton(button: ISubMenuButton) { 44 | this.selectButton(button); 45 | } 46 | 47 | getBackgroundClass(){ 48 | return { 49 | disableBackground: this.options.disableBackground ? true : false 50 | }; 51 | } 52 | 53 | getIconClass(button: ISubMenuButton){ 54 | var iconClass = {}; 55 | if(button.iconClass){ 56 | iconClass[button.iconClass] = true; 57 | } 58 | if(this.options.iconSize){ 59 | iconClass[this.options.iconSize] = true; 60 | } 61 | return iconClass; 62 | } 63 | 64 | 65 | showSubMenu():boolean { 66 | if(!this.options){ 67 | return false; 68 | } 69 | return true; 70 | } 71 | 72 | showText(button: ISubMenuButton): boolean{ 73 | if(this.options.disableText || !button || !button.text){ 74 | return false; 75 | } 76 | return true; 77 | } 78 | 79 | showIcon(button: ISubMenuButton): boolean{ 80 | if(this.options.disableIcons || !button || !button.iconClass){ 81 | return false; 82 | } 83 | return true; 84 | } 85 | 86 | getHint(button: ISubMenuButton): string{ 87 | if(this.options.disableText){ 88 | return button.text; 89 | } 90 | return ''; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/directive/dragAndDrop/dragAndDrop.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostListener, Input, HostBinding } from '@angular/core'; 2 | import { DragAndDropService } from "./dragAndDrop.service"; 3 | 4 | @Directive({ 5 | selector: '[dragstart]' 6 | }) 7 | export class DragStartDirective { 8 | 9 | @Input('dragstart') private dragStartCallback: (DragEvent, DragAndDropService)=>void; 10 | 11 | @HostBinding('draggable') hostDraggable = "true"; 12 | 13 | constructor(private dragAndDropService: DragAndDropService) {} 14 | 15 | @HostListener('dragstart', ['$event']) 16 | onDragStart(event: DragEvent) { 17 | event.dataTransfer.effectAllowed = 'all'; 18 | 19 | this.dragAndDropService.clear(); 20 | this.dragStartCallback(event, this.dragAndDropService); 21 | 22 | } 23 | } 24 | 25 | @Directive({ 26 | selector: '[drag-success][drag-error]' 27 | }) 28 | export class DragSuccessDirective { 29 | 30 | @Input('drag-success') private dragSuccessCallback: (DragEvent, DragAndDropService)=>void; 31 | @Input('drag-error') private dragErrorCallback: (DragEvent, DragAndDropService)=>void; 32 | 33 | constructor(private dragAndDropService: DragAndDropService) {} 34 | 35 | @HostListener('dragend', ['$event']) 36 | onDropSuccess(event: DragEvent) { 37 | 38 | if(this.dragAndDropService.getSuccess() === true){ 39 | this.dragSuccessCallback(event, this.dragAndDropService); 40 | }else{ 41 | this.dragErrorCallback(event, this.dragAndDropService); 42 | } 43 | 44 | this.dragAndDropService.clear(); 45 | 46 | } 47 | } 48 | 49 | @Directive({ 50 | selector: '[dragover]' 51 | }) 52 | export class DragOverDirective { 53 | 54 | @Input('dragover') private dragoverCallback: (DragEvent, DragAndDropService)=>boolean; 55 | 56 | constructor(private dragAndDropService: DragAndDropService) {} 57 | 58 | @HostListener('dragover', ['$event']) 59 | onDropOver(event: DragEvent) { 60 | event.dataTransfer.dropEffect = 'move'; 61 | 62 | if(this.dragoverCallback){ 63 | this.dragoverCallback(event, this.dragAndDropService); 64 | } 65 | 66 | event.preventDefault(); 67 | } 68 | } 69 | 70 | @Directive({ 71 | selector: '[drop]' 72 | }) 73 | export class DropDirective { 74 | 75 | @Input('drop') private dropCallback: (DragEvent, DragAndDropService)=>boolean; 76 | 77 | constructor(private dragAndDropService: DragAndDropService) {} 78 | 79 | @HostListener('drop', ['$event']) 80 | onDrop(event: DragEvent) { 81 | 82 | if(this.dropCallback(event, this.dragAndDropService)){ 83 | this.dragAndDropService.setSuccess(true); 84 | } 85 | 86 | event.preventDefault(); 87 | } 88 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/assets/icons/source/interactor-font-icons/demo-files/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | font-family: sans-serif; 5 | font-size: 1em; 6 | line-height: 1.5; 7 | color: #555; 8 | background: #fff; 9 | } 10 | h1 { 11 | font-size: 1.5em; 12 | font-weight: normal; 13 | } 14 | small { 15 | font-size: .66666667em; 16 | } 17 | a { 18 | color: #e74c3c; 19 | text-decoration: none; 20 | } 21 | a:hover, a:focus { 22 | box-shadow: 0 1px #e74c3c; 23 | } 24 | .bshadow0, input { 25 | box-shadow: inset 0 -2px #e7e7e7; 26 | } 27 | input:hover { 28 | box-shadow: inset 0 -2px #ccc; 29 | } 30 | input, fieldset { 31 | font-family: sans-serif; 32 | font-size: 1em; 33 | margin: 0; 34 | padding: 0; 35 | border: 0; 36 | } 37 | input { 38 | color: inherit; 39 | line-height: 1.5; 40 | height: 1.5em; 41 | padding: .25em 0; 42 | } 43 | input:focus { 44 | outline: none; 45 | box-shadow: inset 0 -2px #449fdb; 46 | } 47 | .glyph { 48 | font-size: 16px; 49 | width: 15em; 50 | padding-bottom: 1em; 51 | margin-right: 4em; 52 | margin-bottom: 1em; 53 | float: left; 54 | overflow: hidden; 55 | } 56 | .liga { 57 | width: 80%; 58 | width: calc(100% - 2.5em); 59 | } 60 | .talign-right { 61 | text-align: right; 62 | } 63 | .talign-center { 64 | text-align: center; 65 | } 66 | .bgc1 { 67 | background: #f1f1f1; 68 | } 69 | .fgc1 { 70 | color: #999; 71 | } 72 | .fgc0 { 73 | color: #000; 74 | } 75 | p { 76 | margin-top: 1em; 77 | margin-bottom: 1em; 78 | } 79 | .mvm { 80 | margin-top: .75em; 81 | margin-bottom: .75em; 82 | } 83 | .mtn { 84 | margin-top: 0; 85 | } 86 | .mtl, .mal { 87 | margin-top: 1.5em; 88 | } 89 | .mbl, .mal { 90 | margin-bottom: 1.5em; 91 | } 92 | .mal, .mhl { 93 | margin-left: 1.5em; 94 | margin-right: 1.5em; 95 | } 96 | .mhmm { 97 | margin-left: 1em; 98 | margin-right: 1em; 99 | } 100 | .mls { 101 | margin-left: .25em; 102 | } 103 | .ptl { 104 | padding-top: 1.5em; 105 | } 106 | .pbs, .pvs { 107 | padding-bottom: .25em; 108 | } 109 | .pvs, .pts { 110 | padding-top: .25em; 111 | } 112 | .unit { 113 | float: left; 114 | } 115 | .unitRight { 116 | float: right; 117 | } 118 | .size1of2 { 119 | width: 50%; 120 | } 121 | .size1of1 { 122 | width: 100%; 123 | } 124 | .clearfix:before, .clearfix:after { 125 | content: " "; 126 | display: table; 127 | } 128 | .clearfix:after { 129 | clear: both; 130 | } 131 | .hidden-true { 132 | display: none; 133 | } 134 | .textbox0 { 135 | width: 3em; 136 | background: #f1f1f1; 137 | padding: .25em .5em; 138 | line-height: 1.5; 139 | height: 1.5em; 140 | } 141 | #testDrive { 142 | display: block; 143 | padding-top: 24px; 144 | line-height: 1.5; 145 | } 146 | .fs0 { 147 | font-size: 16px; 148 | } 149 | .fs1 { 150 | font-size: 32px; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /inc/processing.js: -------------------------------------------------------------------------------- 1 | function processData(midiData) { 2 | console.log(midiData); 3 | 4 | var musicBox = generateMusicBox({letter: "c", octave: 5}, generateScale("c", "major", "ionian"), 20); 5 | 6 | var events = []; 7 | 8 | for(var i=0; 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 -------------------------------------------------------------------------------- /inc/scaleandmusicbox.js: -------------------------------------------------------------------------------- 1 | var noteLetters = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"] 2 | var notes = []; 3 | 4 | for (var num = 0; num < 128; num++) { 5 | notes.push({letter: noteLetters[num%12], octave: Math.floor(num/12)}); 6 | } 7 | 8 | var makerjs = require('makerjs'); 9 | 10 | var scaleModes = { 11 | major: { 12 | ionian: [2,2,1,2,2,2], 13 | dorian: [2,1,2,2,2,1], 14 | phyrigian: [1,2,2,2,1,2], 15 | lydian: [2,2,2,1,2,2], 16 | mixolydian: [2,2,1,2,2,1], 17 | aeolian: [2,1,2,2,1,2], 18 | locrian: [1,2,2,1,2,2] 19 | }, 20 | harmonic_minor: { 21 | harmminor: [2,1,2,2,1,3], 22 | locriansharpsix:[1,2,2,1,3,1], 23 | ionianaug: [2,2,1,3,1,2], 24 | romanian: [2,1,3,1,2,1], 25 | phrygiandom: [1,3,1,2,1,2], 26 | lydiansharptwo: [3,1,2,1,2,2], 27 | ultralocrian: [1,2,1,2,2,1] 28 | }, 29 | melodic_minor: { 30 | jazz_minor: [2,1,2,2,2,2], 31 | dorianflatnine: [1,2,2,2,2,1], 32 | lydianaug: [2,2,2,2,1,2], 33 | lydiandom: [2,2,2,1,2,1], 34 | mixolydianflatsix: [2,2,1,2,1,2], 35 | semilocrian: [2,1,2,1,2,2], 36 | superlocrian: [1,2,1,2,2,2] 37 | } 38 | } 39 | 40 | function generateScale(root, scale, mode) { 41 | var result = []; 42 | var gaps = scaleModes[scale][mode]; 43 | if (gaps === undefined) return undefined; 44 | result.push(root); 45 | var lastNoteNum = noteLetters.indexOf(root); 46 | for (var i=0; i 11 | } 12 | 13 | interface JSZipConstructor { 14 | new (): JSZipInterface 15 | } 16 | 17 | 18 | const module: JSZipConstructor 19 | export = module 20 | } 21 | 22 | //jasmine subset 23 | 24 | //declare var require: any; 25 | declare function describe(description: string, specDefinitions: () => void): void; 26 | declare function fdescribe(description: string, specDefinitions: () => void): void; 27 | declare function xdescribe(description: string, specDefinitions: () => void): void; 28 | 29 | declare function it(expectation: string, assertion?: (done: DoneFn) => void, timeout?: number): void; 30 | declare function fit(expectation: string, assertion?: (done: DoneFn) => void, timeout?: number): void; 31 | declare function xit(expectation: string, assertion?: (done: DoneFn) => void, timeout?: number): void; 32 | 33 | /** If you call the function pending anywhere in the spec body, no matter the expectations, the spec will be marked pending. */ 34 | declare function pending(reason?: string): void; 35 | 36 | declare function beforeEach(action: (done: DoneFn) => void, timeout?: number): void; 37 | declare function afterEach(action: (done: DoneFn) => void, timeout?: number): void; 38 | 39 | declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void; 40 | declare function afterAll(action: (done: DoneFn) => void, timeout?: number): void; 41 | 42 | declare function expect(spy: Function): Matchers; 43 | declare function expect(actual: T): Matchers; 44 | 45 | declare function fail(e?: any): void; 46 | /** Action method that should be called when the async work is complete */ 47 | interface DoneFn extends Function { 48 | (): void; 49 | 50 | /** fails the spec and indicates that it has completed. If the message is an Error, Error.message is used */ 51 | fail: (message?: Error|string) => void; 52 | } 53 | 54 | interface Matchers { 55 | 56 | new (env: any, actual: T, spec: any, isNot?: boolean): any; 57 | 58 | env: any; 59 | actual: T; 60 | spec: any; 61 | isNot?: boolean; 62 | message(): any; 63 | 64 | toBe(expected: any, expectationFailOutput?: any): boolean; 65 | toEqual(expected: any, expectationFailOutput?: any): boolean; 66 | toMatch(expected: string | RegExp, expectationFailOutput?: any): boolean; 67 | toBeDefined(expectationFailOutput?: any): boolean; 68 | toBeUndefined(expectationFailOutput?: any): boolean; 69 | toBeNull(expectationFailOutput?: any): boolean; 70 | toBeNaN(): boolean; 71 | toBeTruthy(expectationFailOutput?: any): boolean; 72 | toBeFalsy(expectationFailOutput?: any): boolean; 73 | toHaveBeenCalled(): boolean; 74 | toHaveBeenCalledWith(...params: any[]): boolean; 75 | toHaveBeenCalledTimes(expected: number): boolean; 76 | toContain(expected: any, expectationFailOutput?: any): boolean; 77 | toBeLessThan(expected: number, expectationFailOutput?: any): boolean; 78 | toBeLessThanOrEqual(expected: number, expectationFailOutput?: any): boolean; 79 | toBeGreaterThan(expected: number, expectationFailOutput?: any): boolean; 80 | toBeGreaterThanOrEqual(expected: number, expectationFailOutput?: any): boolean; 81 | toBeCloseTo(expected: number, precision?: any, expectationFailOutput?: any): boolean; 82 | toThrow(expected?: any): boolean; 83 | toThrowError(message?: string | RegExp): boolean; 84 | toThrowError(expected?: new (...args: any[]) => Error, message?: string | RegExp): boolean; 85 | not: Matchers; 86 | 87 | Any: any; 88 | } -------------------------------------------------------------------------------- /inc/drawing.js: -------------------------------------------------------------------------------- 1 | var fileTexts = { 2 | "svg": null, 3 | "dxf": null, 4 | "txt": null 5 | }; 6 | 7 | function downloadFile(fileName, fileText) { 8 | var dataType = "text/plain"; 9 | console.log(fileName); 10 | if(fileName.includes("svg")) dataText="image/svg"; 11 | 12 | var element = document.createElement('a'); 13 | element.setAttribute('href', "data:" + dataType + ";charset=utf-8," + encodeURIComponent(fileText)); 14 | element.setAttribute('download', fileName); 15 | 16 | element.style.display = 'none'; 17 | document.body.appendChild(element); 18 | 19 | element.click(); 20 | 21 | document.body.removeChild(element); 22 | } 23 | 24 | function generateModel(midiData, holeWidth, holeHeight, horizontalMargin, verticalMargin, stripHeight, units) { 25 | var notesAgainstTime = processData(midiData); 26 | var punchHoles = []; 27 | var origins = ""; 28 | var model = { models: {}}; 29 | var biggestY = 0; 30 | var biggestX = 0; 31 | var rectX = 0; 32 | var rectY = 0; 33 | 34 | 35 | if (document.getElementById("hole-shape").value == "Square"){ 36 | for (var i=0; i biggestX) { 45 | biggestX = rectangle.origin[0]; 46 | } 47 | 48 | if (rectangle.origin[1] > biggestY) { 49 | biggestY = rectangle.origin[1]; 50 | } 51 | } 52 | } else { 53 | for (var i=0; i biggestX) { 62 | biggestX = oval.origin[0]; 63 | } 64 | 65 | if (oval.origin[1] > biggestY) { 66 | biggestY = oval.origin[1]; 67 | } 68 | } 69 | } 70 | rectX = (biggestX + horizontalMargin + holeWidth); 71 | rectY = stripHeight; 72 | 73 | var rectangle = new makerjs.models.Rectangle(rectX, rectY); 74 | rectangle.units = units; 75 | 76 | punchHoles.push(rectangle); 77 | model.models = punchHoles; 78 | return [model, origins]; 79 | } 80 | 81 | function prepareFiles(midiData) { 82 | var units = makerjs.unitType.Millimeter; 83 | if (document.getElementById("units").value == "Inches"){ 84 | units = makerjs.unitType.Inch; 85 | } 86 | var holeWidth = parseFloat(document.getElementById("hole-width").value); 87 | var holeHeight = parseFloat(document.getElementById("hole-height").value); 88 | var hMargin = parseFloat(document.getElementById("horizontal-margins").value); 89 | var vMargin = parseFloat(document.getElementById("vertical-margins").value); 90 | var stripHeight = parseFloat(document.getElementById("strip-height").value); 91 | 92 | var returnArray = generateModel(midiData, holeWidth, holeHeight, hMargin, vMargin, stripHeight, units); 93 | var model = returnArray[0]; 94 | var origins = returnArray[1]; 95 | 96 | fileTexts["svg"] = makerjs.exporter.toSVG(model, {units: units, useSvgPathOnly: false, svgAttrs: {xmlns: "http://www.w3.org/2000/svg"}}); 97 | fileTexts["dxf"] = makerjs.exporter.toDXF(model, {units : units}); 98 | fileTexts["txt"] = origins; 99 | document.getElementById("preview-box").innerHTML = fileTexts["svg"]; 100 | } 101 | -------------------------------------------------------------------------------- /experimental/sandbox/templates/layout_anchors_2.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/contextMenu/contextMenu.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ContextMenuRendererComponent } from "./contextMenuRenderer.component"; 3 | import { IContextMenuOptions } from "./contextMenu.interface"; 4 | 5 | @Injectable() 6 | export class ContextMenuService { 7 | renderer: ContextMenuRendererComponent; 8 | 9 | elementHeight: number; 10 | halfPadding: number; 11 | padding: number; 12 | 13 | setRenderer(contextMenuComponent: ContextMenuRendererComponent) { 14 | if (contextMenuComponent) { 15 | this.renderer = contextMenuComponent; 16 | } 17 | } 18 | 19 | public openContextMenu(options: IContextMenuOptions) { 20 | if (!this.renderer) { 21 | return; 22 | } 23 | this.updatePositions(options); 24 | this.renderer.open(options); 25 | } 26 | 27 | public closeContextMenu() { 28 | this.renderer.close(); 29 | } 30 | 31 | updatePositions(menu: IContextMenuOptions){ 32 | var id = this.renderer.getEncapsulationId(); 33 | 34 | var contextMenuElement = this.createElement('div', id, 'context-menu'); 35 | var element = this.createElement('div', id, 'element'); 36 | var text = this.createElement('div', id, null); 37 | text.innerText = 'Text' 38 | 39 | element.appendChild(text); 40 | contextMenuElement.appendChild(element); 41 | 42 | var appElement = document.getElementsByTagName('app')[0]; 43 | 44 | appElement.appendChild(contextMenuElement); 45 | 46 | var a = contextMenuElement.getBoundingClientRect(); 47 | var b = element.getBoundingClientRect(); 48 | 49 | appElement.removeChild(contextMenuElement); 50 | 51 | var width = a.width; 52 | this.elementHeight = b.height; 53 | this.padding = (a.height - b.height); 54 | this.halfPadding = this.padding / 2; 55 | 56 | menu.width = menu.width ? menu.width : 100; 57 | menu.x = menu.x ? menu.x : 0; 58 | menu.y = menu.y ? menu.y : 0; 59 | 60 | if(menu.menu){ 61 | menu.height = menu.menu.length * this.elementHeight + this.padding; 62 | } 63 | 64 | if(menu.height > window.innerHeight){ 65 | menu.height = window.innerHeight; 66 | } 67 | 68 | if(menu.x + menu.width > window.innerWidth){ 69 | menu.x = window.innerWidth - menu.width; 70 | } 71 | 72 | if(menu.y + menu.height > window.innerHeight){ 73 | menu.y = window.innerHeight - menu.height; 74 | } 75 | 76 | if(menu.menu){ 77 | for(var i = 0; i < menu.menu.length; i++){ 78 | var m = menu.menu[i]; 79 | if(!m){ continue; } 80 | this.calculateMenuSize(m, menu); 81 | } 82 | } 83 | } 84 | 85 | createElement(type: string, attribute: string, className: string){ 86 | var tmp = document.createElement(type); 87 | tmp.setAttribute(attribute, ''); 88 | if(className){ 89 | tmp.classList.add(className); 90 | } 91 | return tmp; 92 | } 93 | 94 | calculateMenuSize(menu: IContextMenuOptions, parent: IContextMenuOptions){ 95 | if(!menu || !parent){ return; } 96 | 97 | menu.width = parent.width; 98 | menu.x = parent.x + menu.width - 1; 99 | 100 | var index = parent.menu.indexOf(menu); 101 | if(index > -1){ 102 | menu.y = parent.y + this.elementHeight * index; 103 | } 104 | 105 | if(menu.menu){ 106 | menu.height = menu.menu.length * this.elementHeight; 107 | }else{ 108 | menu.height = 0; 109 | } 110 | 111 | if(menu.height > window.innerHeight){ 112 | menu.height = window.innerHeight; 113 | } 114 | 115 | //if outside of screen 116 | if(menu.x + menu.width > window.innerWidth){ 117 | menu.x = parent.x - menu.width + 1; 118 | } 119 | 120 | if(menu.y + menu.height > window.innerHeight){ 121 | menu.y = window.innerHeight - menu.height; 122 | } 123 | 124 | if(menu.menu){ 125 | for(var i = 0; i < menu.menu.length; i++){ 126 | var m = menu.menu[i]; 127 | if(!m){ continue; } 128 | this.calculateMenuSize(m, menu); 129 | } 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/tabMenu/tabMenu.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { TabMenuModule, TabMenuComponent, ITabMenuComponentOptions } from "./tabMenu.module"; 3 | 4 | describe('TabMenuComponent', () => { 5 | 6 | let fixture: ComponentFixture; 7 | let comp: TabMenuComponent; 8 | let el: HTMLElement; 9 | 10 | let options: ITabMenuComponentOptions; 11 | 12 | beforeEach(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [TabMenuModule] 15 | }); 16 | 17 | fixture = TestBed.createComponent(TabMenuComponent); 18 | comp = fixture.componentInstance; 19 | el = fixture.debugElement.nativeElement; 20 | 21 | options = { 22 | onInit: () => { }, 23 | buttons: [{ 24 | text: 'First', 25 | callback: () => { 26 | 27 | return true; 28 | } 29 | }, { 30 | text: 'Secound', 31 | callback: () => { 32 | return false; 33 | } 34 | }] 35 | }; 36 | }); 37 | 38 | it('should create the TabMenuComponent', () => { 39 | expect(comp).toBeTruthy(); 40 | }); 41 | 42 | it('should have options.component', () => { 43 | 44 | expect(options.component).toBeFalsy(); 45 | 46 | comp.options = options; 47 | fixture.detectChanges(); 48 | 49 | expect(options.component).toBeTruthy(); 50 | }); 51 | 52 | it('should call onInit when options is defined', () => { 53 | 54 | let onInitCalled = false; 55 | options.buttons = []; 56 | options.onInit = () => { 57 | onInitCalled = true; 58 | } 59 | 60 | expect(onInitCalled).toBeFalsy(); 61 | 62 | comp.options = options; 63 | fixture.detectChanges(); 64 | 65 | expect(onInitCalled).toBeTruthy(); 66 | }); 67 | 68 | it('should be visible when options is defined', () => { 69 | options.buttons = []; 70 | 71 | var submenu = el.getElementsByClassName('tab-menu'); 72 | var buttons = el.getElementsByClassName('button'); 73 | 74 | expect(comp.showTabMenu()).toBeFalsy(); 75 | expect(submenu.length).toBe(0); 76 | expect(buttons.length).toBe(0); 77 | 78 | comp.options = options; 79 | fixture.detectChanges(); 80 | 81 | submenu = el.getElementsByClassName('tab-menu'); 82 | buttons = el.getElementsByClassName('button'); 83 | 84 | expect(comp.showTabMenu()).toBeTruthy(); 85 | expect(submenu.length).toBe(1); 86 | expect(buttons.length).toBe(0); 87 | }); 88 | 89 | it('should have buttons with text when options.buttons is defined', () => { 90 | 91 | var buttons = el.getElementsByClassName('button'); 92 | expect(buttons.length).toBe(0); 93 | 94 | comp.options = options; 95 | fixture.detectChanges(); 96 | 97 | buttons = el.getElementsByClassName('button'); 98 | expect(buttons.length).toBe(2); 99 | 100 | expect(buttons[0].getElementsByTagName('h2')[0].textContent).toBe('First'); 101 | expect(buttons[1].getElementsByTagName('h2')[0].textContent).toBe('Secound'); 102 | 103 | }); 104 | 105 | it('should call button callback when button is clicked', () => { 106 | let buttonCallback = false; 107 | options.buttons[0].callback = () => { 108 | buttonCallback = true; 109 | return true; 110 | } 111 | 112 | expect(buttonCallback).toBeFalsy(); 113 | 114 | comp.options = options; 115 | fixture.detectChanges(); 116 | 117 | expect(buttonCallback).toBeFalsy(); 118 | 119 | let buttons = el.getElementsByTagName('button'); 120 | buttons[0].click(); 121 | 122 | expect(buttonCallback).toBeTruthy(); 123 | }); 124 | 125 | it('should set selected when button is selecable', () => { 126 | expect(comp.selected).toBeFalsy(); 127 | 128 | comp.options = options; 129 | fixture.detectChanges(); 130 | 131 | expect(comp.selected).toBeFalsy(); 132 | 133 | comp.selectButton(0); 134 | 135 | expect(comp.selected.text).toBe('First'); 136 | }); 137 | 138 | it('should set selected when button is clicked', () => { 139 | expect(comp.selected).toBeFalsy(); 140 | 141 | comp.options = options; 142 | fixture.detectChanges(); 143 | 144 | expect(comp.selected).toBeFalsy(); 145 | 146 | el.getElementsByTagName('button')[0].click(); 147 | 148 | expect(comp.selected.text).toBe('First'); 149 | }); 150 | 151 | it('should not set selected when button is not selectable', () => { 152 | expect(comp.selected).toBeFalsy(); 153 | 154 | comp.options = options; 155 | fixture.detectChanges(); 156 | 157 | expect(comp.selected).toBeFalsy(); 158 | 159 | comp.selectButton(1); 160 | 161 | expect(comp.selected).toBeFalsy(); 162 | }); 163 | 164 | }); -------------------------------------------------------------------------------- /experimental/sandbox/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Render Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 |
79 | 80 |
81 |

1: Load Track

82 | 83 |
84 |
85 |

2: Specify Device

86 | 94 |
95 |
96 |

3: Render View (click buttons)

97 | 99 | 101 | 102 | 103 |
104 |
105 |

Click on events

106 | 109 | 110 |
111 | 112 |
113 | 114 |
115 |
116 | 117 |
118 |
119 |
120 |
121 | 122 |
123 |
124 | 125 |
126 | 127 | -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/ui/subMenu/subMenu.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { SubMenuModule, SubMenuComponent, ISubMenuComponentOptions } from './submenu.module'; 3 | import { HintModule } from "../hint/hint.module"; 4 | 5 | describe('SubMenuComponent', () => { 6 | 7 | let fixture: ComponentFixture; 8 | let comp: SubMenuComponent; 9 | let el: HTMLElement; 10 | 11 | let options: ISubMenuComponentOptions; 12 | 13 | beforeEach(() => { 14 | TestBed.configureTestingModule({ 15 | imports: [ SubMenuModule, HintModule.forRoot() ] 16 | }); 17 | 18 | fixture = TestBed.createComponent(SubMenuComponent); 19 | comp = fixture.componentInstance; 20 | el = fixture.debugElement.nativeElement; 21 | 22 | options = { 23 | onInit: ()=>{}, 24 | buttons: [{ 25 | text: 'First', 26 | iconClass: 'int-icon-first', 27 | callback: ()=>{ 28 | this.logger.logDebug('MainMenu clicked: Save'); 29 | } 30 | }, { 31 | text: 'Secound', 32 | iconClass: 'int-icon-secound', 33 | callback: ()=>{ 34 | this.logger.logDebug('MainMenu clicked: Exit'); 35 | } 36 | }] 37 | }; 38 | }); 39 | 40 | it('should create the SubMenuComponent', () => { 41 | expect(comp).toBeTruthy(); 42 | }); 43 | 44 | it('should have options.component', () => { 45 | 46 | expect(options.component).toBeFalsy(); 47 | 48 | comp.options = options; 49 | fixture.detectChanges(); 50 | 51 | expect(options.component).toBeTruthy(); 52 | }); 53 | 54 | it('should call onInit when options is defined', () => { 55 | 56 | let onInitCalled = false; 57 | options.buttons = []; 58 | options.onInit = ()=>{ 59 | onInitCalled = true; 60 | } 61 | 62 | expect(onInitCalled).toBeFalsy(); 63 | 64 | comp.options = options; 65 | fixture.detectChanges(); 66 | 67 | expect(onInitCalled).toBeTruthy(); 68 | }); 69 | 70 | it('should be visible when options is defined', () => { 71 | options.buttons = []; 72 | 73 | var submenu = el.getElementsByClassName('sub-menu'); 74 | var buttons = el.getElementsByClassName('button'); 75 | 76 | expect(comp.showSubMenu()).toBeFalsy(); 77 | expect(submenu.length).toBe(0); 78 | expect(buttons.length).toBe(0); 79 | 80 | comp.options = options; 81 | fixture.detectChanges(); 82 | 83 | submenu = el.getElementsByClassName('sub-menu'); 84 | buttons = el.getElementsByClassName('button'); 85 | 86 | expect(comp.showSubMenu()).toBeTruthy(); 87 | expect(submenu.length).toBe(1); 88 | expect(buttons.length).toBe(0); 89 | }); 90 | 91 | it('should have buttons with text when options.buttons is defined', () => { 92 | 93 | var buttons = el.getElementsByClassName('button'); 94 | expect(buttons.length).toBe(0); 95 | 96 | comp.options = options; 97 | fixture.detectChanges(); 98 | 99 | buttons = el.getElementsByClassName('button'); 100 | expect(buttons.length).toBe(2); 101 | 102 | expect(buttons[0].getElementsByTagName('h4')[0].textContent).toBe('First'); 103 | expect(buttons[1].getElementsByTagName('h4')[0].textContent).toBe('Secound'); 104 | 105 | }); 106 | 107 | it('should have buttons with icons when options.buttons is defined', () => { 108 | 109 | var buttons = el.getElementsByClassName('button'); 110 | expect(buttons.length).toBe(0); 111 | 112 | comp.options = options; 113 | fixture.detectChanges(); 114 | 115 | buttons = el.getElementsByClassName('button'); 116 | expect(buttons.length).toBe(2); 117 | 118 | expect(buttons[0].getElementsByTagName('i')[0].classList[0]).toBe('int-icon-first'); 119 | expect(buttons[1].getElementsByTagName('i')[0].classList[0]).toBe('int-icon-secound'); 120 | 121 | }); 122 | 123 | it('should have buttons with no icons when options.disableIcons = true', () => { 124 | options.disableIcons = true; 125 | comp.options = options; 126 | fixture.detectChanges(); 127 | 128 | var buttons = el.getElementsByClassName('button'); 129 | expect(buttons.length).toBe(2); 130 | 131 | expect(el.getElementsByTagName('i').length).toBe(0); 132 | }); 133 | 134 | it('should have buttons with no text when options.disableText = true', () => { 135 | options.disableText = true; 136 | comp.options = options; 137 | fixture.detectChanges(); 138 | 139 | var buttons = el.getElementsByClassName('button'); 140 | expect(buttons.length).toBe(2); 141 | 142 | expect(el.getElementsByTagName('h4').length).toBe(0); 143 | }); 144 | 145 | it('should call button callback when button is clicked', () => { 146 | let buttonCallback = false; 147 | options.buttons[0].callback = ()=>{ 148 | buttonCallback = true; 149 | } 150 | 151 | expect(buttonCallback).toBeFalsy(); 152 | 153 | comp.options = options; 154 | fixture.detectChanges(); 155 | 156 | expect(buttonCallback).toBeFalsy(); 157 | 158 | let buttons = el.getElementsByTagName('button'); 159 | buttons[0].click(); 160 | 161 | expect(buttonCallback).toBeTruthy(); 162 | }); 163 | }); -------------------------------------------------------------------------------- /inc/user-interface.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | 3 | var fields = [ 4 | { name: 'units', type: 'select', id: 'units' }, 5 | { name: 'workpieceWidth', type: 'text', id: 'workpiece-width' }, 6 | { name: 'workpieceHeight', type: 'text', id: 'workpiece-height' }, 7 | { name: 'stripHeight', type: 'text', id: 'strip-height' }, 8 | { name: 'scale', type: 'select', id: 'scale' }, 9 | { name: 'scaleType', type: 'select', id: 'scale-type' }, 10 | 11 | { name: 'startingEdge', type: 'select', id: 'starting-edge' }, 12 | { name: 'jointEdge', type: 'select', id: 'joint-edge' }, 13 | { name: 'endingEdge', type: 'select', id: 'ending-edge' }, 14 | { name: 'amountOfPins', type: 'text', id: 'amount-of-pins' }, 15 | 16 | { name: 'holeShape', type: 'select', id: 'hole-shape' }, 17 | { name: 'holeWidth', type: 'text', id: 'hole-width' }, 18 | { name: 'holeHeight', type: 'text', id: 'hole-height' }, 19 | { name: 'horizontalMargins', type: 'text', id: 'horizontal-margins' }, 20 | { name: 'verticalMargins', type: 'text', id: 'vertical-margins' }, 21 | 22 | { name: 'exportFormat', type: 'select', id: 'export-format' }, 23 | { name: 'lineColor', type: 'text', id: 'line-color' } 24 | ]; 25 | 26 | $("#units").change(function () { 27 | if ($("#units option:selected").text() == "Inches") { 28 | $('.unit-suffix').text('in'); 29 | } else { 30 | $('.unit-suffix').text('mm'); 31 | } 32 | 33 | }); 34 | 35 | $("#file-picker-midi").change(function () { 36 | var fileName = $(this).val(); 37 | $("#midi-file").val(fileName.substring(fileName.lastIndexOf("\\") + 1, fileName.length)); 38 | }); 39 | 40 | $("#line-color").change(function () { 41 | var lineColor = $(this).val().toUpperCase(); 42 | $("#color-suffix").text(lineColor); 43 | }); 44 | 45 | $("#import-settings").click(function () { 46 | if (confirm('Are you sure you want to overwrite your current configuration?')) { 47 | $("#file-picker-import-settings").trigger('click'); 48 | } 49 | }); 50 | 51 | $("#file-picker-import-settings").change(function () { 52 | var file = $('#file-picker-import-settings')[0].files[0]; 53 | importSettings(file); 54 | }); 55 | 56 | $("#export-settings").click(function () { 57 | var settings = { }; 58 | fields.forEach(function (field) { 59 | settings[field.name] = $('#' + field.id).val(); 60 | }); 61 | 62 | console.log(settings); 63 | 64 | json_export = JSON.stringify(settings); 65 | console.log(settings); 66 | console.log(json_export); 67 | 68 | var element = document.createElement('a'); 69 | element.setAttribute('href', "data:application/json;charset=utf-8," + encodeURIComponent(json_export)); 70 | element.setAttribute('download', 'settings.json'); 71 | 72 | element.style.display = 'none'; 73 | document.body.appendChild(element); 74 | 75 | element.click(); 76 | 77 | document.body.removeChild(element); 78 | 79 | }); 80 | 81 | //MIDIParser.addListener(document.getElementById('file-picker-midi'), function (data) { prepareFiles(data) }); 82 | //console.log("listener added"); 83 | 84 | $("#show-preview").click(function () { 85 | if (! validateForm()) { 86 | return; 87 | } 88 | var file = $('#file-picker-midi')[0].files[0]; 89 | if (loadMidiFile(file)) { 90 | $("#download-btn").prop('disabled', false) 91 | } 92 | }); 93 | 94 | $("#download-btn").click(function () { 95 | var value = document.getElementById('export-format').options[document.getElementById('export-format').selectedIndex].value; 96 | downloadFile('data.' + value, fileTexts[value]); 97 | }); 98 | 99 | function importSettings (file) { 100 | if (!file) { 101 | console.log("No settings file selected") 102 | return false; 103 | } 104 | console.log('Attempting to load settings from ' + file.name); 105 | var reader = new FileReader(); 106 | reader.readAsText(file); 107 | reader.onload = function (e) { 108 | var settings = JSON.parse(e.target.result); 109 | console.log(settings); 110 | 111 | fields.forEach(function (field) { 112 | if (settings[field.name]) { 113 | if (field.type === 'text') { 114 | $('#' + field.id).val(settings[field.name]); 115 | } 116 | else if (field.type === 'select') { 117 | $('#' + field.id).val(settings[field.name]).change(); 118 | } 119 | else { 120 | console.log('Invalid field type: ' + field.type); 121 | } 122 | } 123 | }); 124 | }; 125 | } 126 | 127 | function loadMidiFile(file) { 128 | if (!file) { 129 | console.log("No Midi file selected") 130 | return false; 131 | } 132 | console.log('Uploading file detected in INPUT ELEMENT, processing data..'); 133 | var reader = new FileReader(); 134 | reader.readAsArrayBuffer(file); 135 | reader.onload = function (e) { 136 | console.log(e.target.result); 137 | prepareFiles(MIDIParser.Uint8(new Uint8Array(e.target.result))); 138 | }; 139 | return true; 140 | } 141 | 142 | function validateForm() { 143 | // For now, simply verify the user has selected a MIDI file 144 | // Will add min/max, defaults, required/optional once those attributes are identified by the team 145 | 146 | var file = $('#file-picker-midi')[0].files[0]; 147 | if (!file) { 148 | alert('Please select a valid MIDI file.') 149 | return; 150 | } 151 | 152 | return true; 153 | } 154 | }); -------------------------------------------------------------------------------- /experimental/sandbox/templates/layout_anchors: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /experimental/musicboxeditor/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | Musicbox Editor 5 |

6 | 7 | 8 | 9 |
10 | 91 | 92 |
93 | 94 | 95 | 96 |
97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 119 | 122 | 123 | 124 | 125 | 126 | 129 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
146 |
147 |
148 | 149 | 150 | 151 | > 152 | 153 | -------------------------------------------------------------------------------- /experimental/sandbox/styles/device_concept.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | } 4 | 5 | #container { 6 | position: relative; 7 | width: 1200px; 8 | } 9 | 10 | #device { 11 | position: relative; 12 | float: left; 13 | width: 610px; 14 | min-height: 600px; 15 | 16 | } 17 | 18 | #info { 19 | position: relative; 20 | width: 400px; 21 | height: 600px; 22 | float: left; 23 | margin-left: 20px; 24 | } 25 | 26 | #info-text { 27 | width: 500px; 28 | height: 800px; 29 | } 30 | 31 | #device-controls { 32 | position: relative; 33 | border: 1px solid darkgray; 34 | padding-bottom: 10px; 35 | } 36 | 37 | #device-header { 38 | position: relative; 39 | height: 50px; 40 | border: 1px solid darkgray; 41 | border-radius: 10px; 42 | width: 580px; 43 | margin-top: 10px; 44 | margin-left: 10px; 45 | padding-top: 10px; 46 | font-size: 12px; 47 | padding-bottom: 50px; 48 | } 49 | 50 | #device-id-label { 51 | position: absolute; 52 | top: 10px; 53 | left: 200px; 54 | width: 50px; 55 | } 56 | 57 | #device-id-label input { 58 | width: 50px; 59 | } 60 | 61 | #device-deviceName-label { 62 | position: absolute; 63 | top: 10px; 64 | left: 10px; 65 | width: 100px; 66 | } 67 | 68 | #device-description-label { 69 | position: absolute; 70 | top: 55px; 71 | left: 10px; 72 | width: 450px; 73 | } 74 | 75 | #device-description-label input { 76 | width: 450px; 77 | } 78 | 79 | #device-select-label { 80 | position: absolute; 81 | top: 10px; 82 | left: 300px; 83 | width: 160px; 84 | } 85 | 86 | #device-select-label select { 87 | margin-top: 2px; 88 | width: 160px; 89 | } 90 | 91 | #device-channels-header { 92 | position: relative; 93 | height: 20px; 94 | margin-left: 5px; 95 | font-size: 14px; 96 | padding-bottom: 5px; 97 | } 98 | 99 | #device-channels-header p { 100 | margin: 0; 101 | padding: 0; 102 | } 103 | 104 | #device-channels { 105 | position: relative; 106 | border: 1px solid darkgray; 107 | border-radius: 10px; 108 | width: 590px; 109 | margin-top: 10px; 110 | margin-left: 10px; 111 | padding-top: 10px; 112 | font-size: 12px; 113 | padding-bottom: 10px; 114 | } 115 | 116 | #channel-columns { 117 | position: relative; 118 | padding-left: 10px; 119 | width: 520px; 120 | height: 20px; 121 | } 122 | 123 | .device-channel { 124 | width: 590px; 125 | height: 20px; 126 | padding-left: 5px; 127 | padding-bottom: 5px; 128 | padding-top: 0; 129 | 130 | } 131 | 132 | #channels-footer { 133 | margin-top: 20px; 134 | padding-top: 10px; 135 | height: 60px; 136 | padding-left: 5px; 137 | border-top: 1px solid darkgray; 138 | } 139 | 140 | #channel-add { 141 | margin-bottom: 5px; 142 | } 143 | 144 | .channel-noteNumber, .channel-name, .channel-description, .channel-offset, .channel-height, .channel-shape { 145 | margin-top: 0; 146 | margin-bottom: 0; 147 | display: inline-block; 148 | } 149 | 150 | .channel-noteNumber { 151 | width: 80px; 152 | } 153 | 154 | .channel-name { 155 | width: 100px; 156 | } 157 | 158 | .channel-description { 159 | width: 200px; 160 | } 161 | 162 | .channel-offset { 163 | width: 50px; 164 | } 165 | 166 | .channel-height { 167 | width: 50px; 168 | } 169 | 170 | .channel-shape { 171 | width: 50px; 172 | } 173 | 174 | .channel-delete { 175 | font-size: 10px; 176 | line-height: 10px; 177 | padding: 0; 178 | width: 22px; 179 | height: 20px; 180 | 181 | } 182 | 183 | .channel-shape-select { 184 | width: 50px; 185 | } 186 | 187 | .channel-move-btns { 188 | position: relative; 189 | display: inline-block; 190 | margin: 0; 191 | margin-left: 2px; 192 | width: 22px; 193 | height: 20px; 194 | 195 | } 196 | 197 | .channel-up, .channel-down { 198 | font-size: 10px; 199 | line-height: 10px; 200 | margin: 0; 201 | padding: 0; 202 | height: 12px; 203 | } 204 | 205 | .channel-up { 206 | position: absolute; 207 | top: 2px; 208 | left: 0; 209 | width: 22px; 210 | 211 | } 212 | 213 | .channel-down { 214 | position: absolute; 215 | top: 13px; 216 | left: 0; 217 | width: 22px; 218 | 219 | } 220 | 221 | #device-dimensions { 222 | position: relative; 223 | border: 1px solid darkgray; 224 | border-radius: 10px; 225 | width: 580px; 226 | margin-top: 10px; 227 | margin-left: 10px; 228 | padding-top: 10px; 229 | font-size: 12px; 230 | padding-bottom: 10px; 231 | } 232 | 233 | .margin-label { 234 | width: 50px; 235 | } 236 | 237 | .margin-label input { 238 | width: 50px; 239 | } 240 | 241 | #strip-preview { 242 | position: relative; 243 | padding: 3px; 244 | width: 520px; 245 | height: 255px; 246 | margin-left: 20px; 247 | margin-bottom: 15px; 248 | 249 | } 250 | 251 | #strip-rect { 252 | position: absolute; 253 | top: 30px; 254 | left: 70px; 255 | border: 1px solid black; 256 | width: 400px; 257 | height: 200px; 258 | } 259 | 260 | #device-topPadding-label { 261 | position: absolute; 262 | top: 5px; 263 | left: 140px; 264 | width: 200px; 265 | height: 50px; 266 | } 267 | 268 | #device-rightPadding-label { 269 | position: absolute; 270 | top: 100px; 271 | left: 480px; 272 | width: 60px; 273 | height: 50px; 274 | } 275 | 276 | #device-bottomPadding-label { 277 | position: absolute; 278 | top: 235px; 279 | left: 140px; 280 | width: 200px; 281 | height: 50px; 282 | } 283 | 284 | #device-leftPadding-label { 285 | position: absolute; 286 | top: 100px; 287 | left: 5px; 288 | width: 60px; 289 | height: 50px; 290 | } 291 | 292 | #show-rounded-label { 293 | position: absolute; 294 | top: 2px; 295 | left: 430px; 296 | width: 120px; 297 | height: 20px; 298 | } 299 | 300 | #hole-settings { 301 | position: relative; 302 | width: 580px; 303 | margin-top: 0; 304 | margin-left: 10px; 305 | padding-top: 0; 306 | padding-left: 30px; 307 | } 308 | 309 | #hole-settings label { 310 | display: inline-block; 311 | width: 110px; 312 | margin-bottom: 10px; 313 | margin-left: 10px; 314 | height: 40px; 315 | } 316 | 317 | #hole-settings label input { 318 | width: 80px; 319 | } 320 | 321 | input[readonly] { 322 | background-color: lightgray; 323 | border: 1px solid darkgray; 324 | padding: 2px; 325 | } 326 | 327 | #new-device { 328 | position: relative; 329 | float: left; 330 | margin-left: 20px; 331 | width: 500px; 332 | height: 220px; 333 | } 334 | 335 | #new-device-controls { 336 | position: relative; 337 | border: 1px solid darkgray; 338 | border-radius: 10px; 339 | min-height: 220px; 340 | 341 | } 342 | 343 | #new-device-id-label { 344 | position: absolute; 345 | top: 10px; 346 | left: 10px; 347 | width: 110px; 348 | height: 20px; 349 | } 350 | 351 | #new-device-id { 352 | width: 100px; 353 | } 354 | 355 | #new-device-channel-count-label { 356 | position: absolute; 357 | top: 10px; 358 | left: 150px; 359 | width: 150px; 360 | height: 20px; 361 | } 362 | 363 | #new-device-channel-count { 364 | width: 60px; 365 | } 366 | 367 | #new-device-start-note-label { 368 | position: absolute; 369 | top: 60px; 370 | left: 10px; 371 | width: 100px; 372 | height: 20px; 373 | } 374 | 375 | #new-device-start-note { 376 | width: 60px; 377 | } 378 | 379 | #new-device-key-label { 380 | position: absolute; 381 | top: 110px; 382 | left: 10px; 383 | width: 40px; 384 | height: 20px; 385 | } 386 | 387 | #new-device-key { 388 | width: 60px; 389 | } 390 | 391 | #new-device-scale-label { 392 | position: absolute; 393 | top: 110px; 394 | left: 80px; 395 | width: 40px; 396 | height: 20px; 397 | } 398 | 399 | #new-device-scale { 400 | width: 130px; 401 | } 402 | 403 | #new-device-mode-label { 404 | position: absolute; 405 | top: 110px; 406 | left: 220px; 407 | width: 40px; 408 | height: 20px; 409 | } 410 | 411 | #new-device-mode { 412 | width: 130px; 413 | } 414 | 415 | #new-device-add-btn-label { 416 | position: absolute; 417 | bottom: 10px; 418 | left: 10px; 419 | width: 40px; 420 | height: 20px; 421 | } 422 | 423 | --------------------------------------------------------------------------------