├── .gitignore ├── README.md ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── modules.js ├── paths.js ├── pnpTs.js ├── webpack.config.js └── webpackDevServer.config.js ├── package.json ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── code-bg.jpg ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html ├── ogg │ ├── bass │ │ ├── boffner1.ogg │ │ └── boffner2.ogg │ ├── cello │ │ ├── cello-a2.ogg │ │ ├── cello-a2.wav │ │ ├── cello-a3.ogg │ │ ├── cello-a3.wav │ │ ├── cello-a4.ogg │ │ ├── cello-a4.wav │ │ ├── cello-a5.ogg │ │ ├── cello-a5.wav │ │ ├── cello-c2.ogg │ │ ├── cello-c2.wav │ │ ├── cello-c3.ogg │ │ ├── cello-c3.wav │ │ ├── cello-c4.ogg │ │ ├── cello-c4.wav │ │ ├── cello-c5.ogg │ │ ├── cello-c5.wav │ │ ├── cello-ds2.ogg │ │ ├── cello-ds2.wav │ │ ├── cello-ds3.ogg │ │ ├── cello-ds3.wav │ │ ├── cello-ds4.ogg │ │ ├── cello-ds4.wav │ │ ├── cello-ds5.ogg │ │ ├── cello-ds5.wav │ │ ├── cello-fs2.ogg │ │ ├── cello-fs2.wav │ │ ├── cello-fs3.ogg │ │ ├── cello-fs3.wav │ │ ├── cello-fs4.ogg │ │ ├── cello-fs4.wav │ │ ├── cello-fs5.ogg │ │ └── cello-fs5.wav │ ├── chorus │ │ ├── chorus-female-a4.ogg │ │ ├── chorus-female-a4.wav │ │ ├── chorus-female-a5.ogg │ │ ├── chorus-female-a5.wav │ │ ├── chorus-female-as4.ogg │ │ ├── chorus-female-as4.wav │ │ ├── chorus-female-as5.ogg │ │ ├── chorus-female-as5.wav │ │ ├── chorus-female-b4.ogg │ │ ├── chorus-female-b4.wav │ │ ├── chorus-female-b5.ogg │ │ ├── chorus-female-b5.wav │ │ ├── chorus-female-c5.ogg │ │ ├── chorus-female-c5.wav │ │ ├── chorus-female-c6.ogg │ │ ├── chorus-female-c6.wav │ │ ├── chorus-female-cs5.ogg │ │ ├── chorus-female-cs5.wav │ │ ├── chorus-female-d5.ogg │ │ ├── chorus-female-d5.wav │ │ ├── chorus-female-ds5.ogg │ │ ├── chorus-female-ds5.wav │ │ ├── chorus-female-e5.ogg │ │ ├── chorus-female-e5.wav │ │ ├── chorus-female-f5.ogg │ │ ├── chorus-female-f5.wav │ │ ├── chorus-female-fs5.ogg │ │ ├── chorus-female-fs5.wav │ │ ├── chorus-female-g4.ogg │ │ ├── chorus-female-g4.wav │ │ ├── chorus-female-g5.ogg │ │ ├── chorus-female-g5.wav │ │ ├── chorus-female-gs4.ogg │ │ ├── chorus-female-gs4.wav │ │ ├── chorus-female-gs5.ogg │ │ └── chorus-female-gs5.wav │ ├── drums │ │ ├── Clap.ogg │ │ ├── ClosedHat.ogg │ │ ├── Kick.ogg │ │ ├── OpenHat.ogg │ │ ├── pedalD1.ogg │ │ ├── pedalD2.ogg │ │ ├── pedalU1.ogg │ │ └── pedalU2.ogg │ ├── flute │ │ ├── flute-a3.ogg │ │ ├── flute-a3.wav │ │ ├── flute-a4.ogg │ │ ├── flute-a4.wav │ │ ├── flute-a5.ogg │ │ ├── flute-a5.wav │ │ ├── flute-c3.ogg │ │ ├── flute-c3.wav │ │ ├── flute-c4.ogg │ │ ├── flute-c4.wav │ │ ├── flute-c5.ogg │ │ ├── flute-c5.wav │ │ ├── flute-c6.ogg │ │ ├── flute-c6.wav │ │ ├── flute-ds3.ogg │ │ ├── flute-ds3.wav │ │ ├── flute-ds4.ogg │ │ ├── flute-ds4.wav │ │ ├── flute-ds5.ogg │ │ ├── flute-ds5.wav │ │ ├── flute-fs3.ogg │ │ ├── flute-fs3.wav │ │ ├── flute-fs4.ogg │ │ ├── flute-fs4.wav │ │ ├── flute-fs5.ogg │ │ └── flute-fs5.wav │ ├── harp │ │ ├── harp-a2.ogg │ │ ├── harp-a2.wav │ │ ├── harp-a3.ogg │ │ ├── harp-a3.wav │ │ ├── harp-a4.ogg │ │ ├── harp-a4.wav │ │ ├── harp-a5.ogg │ │ ├── harp-a5.wav │ │ ├── harp-a6.ogg │ │ ├── harp-a6.wav │ │ ├── harp-c2.ogg │ │ ├── harp-c2.wav │ │ ├── harp-c3.ogg │ │ ├── harp-c3.wav │ │ ├── harp-c4.ogg │ │ ├── harp-c4.wav │ │ ├── harp-c5.ogg │ │ ├── harp-c5.wav │ │ ├── harp-c6.ogg │ │ ├── harp-c6.wav │ │ ├── harp-c7.ogg │ │ ├── harp-c7.wav │ │ ├── harp-ds2.ogg │ │ ├── harp-ds2.wav │ │ ├── harp-ds3.ogg │ │ ├── harp-ds3.wav │ │ ├── harp-ds4.ogg │ │ ├── harp-ds4.wav │ │ ├── harp-ds5.ogg │ │ ├── harp-ds5.wav │ │ ├── harp-ds6.ogg │ │ ├── harp-ds6.wav │ │ ├── harp-fs2.ogg │ │ ├── harp-fs2.wav │ │ ├── harp-fs3.ogg │ │ ├── harp-fs3.wav │ │ ├── harp-fs4.ogg │ │ ├── harp-fs4.wav │ │ ├── harp-fs5.ogg │ │ ├── harp-fs5.wav │ │ ├── harp-fs6.ogg │ │ └── harp-fs6.wav │ ├── piano │ │ ├── A0v10.ogg │ │ ├── A1v10.ogg │ │ ├── A2v10.ogg │ │ ├── A3v10.ogg │ │ ├── A4v10.ogg │ │ ├── A5v10.ogg │ │ ├── A6v10.ogg │ │ ├── A7v10.ogg │ │ ├── C1v10.ogg │ │ ├── C2v10.ogg │ │ ├── C3v10.ogg │ │ ├── C4v10.ogg │ │ ├── C5v10.ogg │ │ ├── C6v10.ogg │ │ ├── C7v10.ogg │ │ ├── C8v10.ogg │ │ ├── Ds1v10.ogg │ │ ├── Ds2v10.ogg │ │ ├── Ds3v10.ogg │ │ ├── Ds4v10.ogg │ │ ├── Ds5v10.ogg │ │ ├── Ds6v10.ogg │ │ ├── Ds7v10.ogg │ │ ├── Fs1v10.ogg │ │ ├── Fs2v10.ogg │ │ ├── Fs3v10.ogg │ │ ├── Fs4v10.ogg │ │ ├── Fs5v10.ogg │ │ ├── Fs6v10.ogg │ │ └── Fs7v10.ogg │ └── tuba-stc │ │ ├── tuba-stc-rr1-as1.ogg │ │ ├── tuba-stc-rr1-as1.wav │ │ ├── tuba-stc-rr1-as2.ogg │ │ ├── tuba-stc-rr1-as2.wav │ │ ├── tuba-stc-rr1-as3.ogg │ │ ├── tuba-stc-rr1-as3.wav │ │ ├── tuba-stc-rr1-cs2.ogg │ │ ├── tuba-stc-rr1-cs2.wav │ │ ├── tuba-stc-rr1-cs3.ogg │ │ ├── tuba-stc-rr1-cs3.wav │ │ ├── tuba-stc-rr1-cs4.ogg │ │ ├── tuba-stc-rr1-cs4.wav │ │ ├── tuba-stc-rr1-e1.ogg │ │ ├── tuba-stc-rr1-e1.wav │ │ ├── tuba-stc-rr1-e2.ogg │ │ ├── tuba-stc-rr1-e2.wav │ │ ├── tuba-stc-rr1-e3.ogg │ │ ├── tuba-stc-rr1-e3.wav │ │ ├── tuba-stc-rr1-g1.ogg │ │ ├── tuba-stc-rr1-g1.wav │ │ ├── tuba-stc-rr1-g2.ogg │ │ ├── tuba-stc-rr1-g2.wav │ │ ├── tuba-stc-rr1-g3.ogg │ │ └── tuba-stc-rr1-g3.wav ├── play-bg.jpg ├── preloader-bg.jpg ├── robots.txt ├── site.webmanifest ├── social.png └── twitter.png ├── scripts ├── build.js ├── start.js └── test.js ├── src ├── classes │ ├── AudioRecorder.ts │ ├── Conductor.ts │ └── Orchestra.ts ├── components │ ├── About.tsx │ ├── App.tsx │ ├── CodeEditor.tsx │ ├── CodePage.tsx │ ├── Credits.tsx │ ├── OrchestraContext.tsx │ ├── PlayPage.tsx │ ├── Preloader.tsx │ └── Visualizer.tsx ├── defaultSource.ts ├── index.css ├── index.tsx ├── instruments │ ├── Instrument.ts │ ├── bass.ts │ ├── cello.ts │ ├── chorusFemale.ts │ ├── drums.ts │ ├── flute.ts │ ├── harp.ts │ ├── piano.ts │ └── tuba-stc.ts ├── isDebug.ts ├── players │ ├── createNotesPlayer.ts │ └── createTimeNotesPlayer.ts ├── react-app-env.d.ts ├── serviceWorker.ts ├── setupTests.ts ├── types.ts ├── utils │ ├── calculateMood.ts │ ├── calculateProgression.ts │ ├── createArpeggio.ts │ ├── getChordHarmonic.ts │ ├── index.ts │ └── roman.ts └── words │ ├── negative.json │ └── positive.json ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | resources -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

𝄞 How does your code sound?

2 | 3 |

And the idea was born...

4 | 5 |

6 | 7 | twitter badge 8 | 9 | 10 | linkedin badge 11 | 12 | dev.to badge 13 | 14 | 15 | medium badge 16 | 17 |

18 | 19 |
20 | 21 |

About the idea

22 | 23 | As a developer, I love listening to music while coding. The orchestral music allows me to focus more on what I do. And one day I noticed my fingers dance on the keyboard by the music rhythm. Like playing the piano. Every word or symbol in the code was written with harmony. And then I thought... how it could sound... The code I write every day? 24 | 25 | _And the idea was born._ 26 | 27 |
28 | 29 | > Put your code and enjoy how it sounds: [soundcode.now.sh](https://soundcode.now.sh/) 30 | 31 | Have ideas on how to improve it? New features? Feel free to share it on the [GitHub Issues](https://github.com/epranka/soundcode/issues). 32 | 33 |

34 | sound code gif 35 |

36 | 37 |

How it works

38 | 39 | Firstly, we load the sound fonts of the instruments which are used in this little orchestra. When you paste or write your code (or using our example), we parse it using the TypeScript AST parser to individual nodes. Then the composition begins. 40 | 41 |

The mood of the code

42 | By code source, we determine the mood of the code. The more cheerful words in the code, the happier the mood and vice versa. The mood of the code is used to set the musical scale. If happy, a Major will be likely selected, if sad - Minor. 43 | 44 |

Chords

45 | By the code source and with some easy math we choose which chords progression play from the determined musical scale. 46 | 47 |

The Melody of the piano

48 | Each piano note is the TypeScript Token. With some math, we set the note, pitch, duration and time when to play. The special symbols like ,.+-/*, etc are excluded and used in the other instrument 49 | 50 |

Other instruments

51 | Each instrument has its notes. Some just looping the notes of the chord, while others play specific notes by the source code. For example, the Cello always plays the active chord root note, when Harp only plays at the special characters or Chorus at the strings. 52 | -------------------------------------------------------------------------------- /config/env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve('./paths')]; 9 | 10 | const NODE_ENV = process.env.NODE_ENV; 11 | if (!NODE_ENV) { 12 | throw new Error( 13 | 'The NODE_ENV environment variable is required but was not specified.' 14 | ); 15 | } 16 | 17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 18 | const dotenvFiles = [ 19 | `${paths.dotenv}.${NODE_ENV}.local`, 20 | `${paths.dotenv}.${NODE_ENV}`, 21 | // Don't include `.env.local` for `test` environment 22 | // since normally you expect tests to produce the same 23 | // results for everyone 24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 25 | paths.dotenv, 26 | ].filter(Boolean); 27 | 28 | // Load environment variables from .env* files. Suppress warnings using silent 29 | // if this file is missing. dotenv will never modify any environment variables 30 | // that have already been set. Variable expansion is supported in .env files. 31 | // https://github.com/motdotla/dotenv 32 | // https://github.com/motdotla/dotenv-expand 33 | dotenvFiles.forEach(dotenvFile => { 34 | if (fs.existsSync(dotenvFile)) { 35 | require('dotenv-expand')( 36 | require('dotenv').config({ 37 | path: dotenvFile, 38 | }) 39 | ); 40 | } 41 | }); 42 | 43 | // We support resolving modules according to `NODE_PATH`. 44 | // This lets you use absolute paths in imports inside large monorepos: 45 | // https://github.com/facebook/create-react-app/issues/253. 46 | // It works similar to `NODE_PATH` in Node itself: 47 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 48 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 49 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 50 | // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 51 | // We also resolve them to make sure all tools using them work consistently. 52 | const appDirectory = fs.realpathSync(process.cwd()); 53 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 54 | .split(path.delimiter) 55 | .filter(folder => folder && !path.isAbsolute(folder)) 56 | .map(folder => path.resolve(appDirectory, folder)) 57 | .join(path.delimiter); 58 | 59 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 60 | // injected into the application via DefinePlugin in Webpack configuration. 61 | const REACT_APP = /^REACT_APP_/i; 62 | 63 | function getClientEnvironment(publicUrl) { 64 | const raw = Object.keys(process.env) 65 | .filter(key => REACT_APP.test(key)) 66 | .reduce( 67 | (env, key) => { 68 | env[key] = process.env[key]; 69 | return env; 70 | }, 71 | { 72 | // Useful for determining whether we’re running in production mode. 73 | // Most importantly, it switches React into the correct mode. 74 | NODE_ENV: process.env.NODE_ENV || 'development', 75 | // Useful for resolving the correct path to static assets in `public`. 76 | // For example, . 77 | // This should only be used as an escape hatch. Normally you would put 78 | // images into the `src` and `import` them in code to get their paths. 79 | PUBLIC_URL: publicUrl, 80 | } 81 | ); 82 | // Stringify all values so we can feed into Webpack DefinePlugin 83 | const stringified = { 84 | 'process.env': Object.keys(raw).reduce((env, key) => { 85 | env[key] = JSON.stringify(raw[key]); 86 | return env; 87 | }, {}), 88 | }; 89 | 90 | return { raw, stringified }; 91 | } 92 | 93 | module.exports = getClientEnvironment; 94 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const camelcase = require('camelcase'); 5 | 6 | // This is a custom Jest transformer turning file imports into filenames. 7 | // http://facebook.github.io/jest/docs/en/webpack.html 8 | 9 | module.exports = { 10 | process(src, filename) { 11 | const assetFilename = JSON.stringify(path.basename(filename)); 12 | 13 | if (filename.match(/\.svg$/)) { 14 | // Based on how SVGR generates a component name: 15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 16 | const pascalCaseFilename = camelcase(path.parse(filename).name, { 17 | pascalCase: true, 18 | }); 19 | const componentName = `Svg${pascalCaseFilename}`; 20 | return `const React = require('react'); 21 | module.exports = { 22 | __esModule: true, 23 | default: ${assetFilename}, 24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) { 25 | return { 26 | $$typeof: Symbol.for('react.element'), 27 | type: 'svg', 28 | ref: ref, 29 | key: null, 30 | props: Object.assign({}, props, { 31 | children: ${assetFilename} 32 | }) 33 | }; 34 | }), 35 | };`; 36 | } 37 | 38 | return `module.exports = ${assetFilename};`; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /config/modules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | const chalk = require('react-dev-utils/chalk'); 7 | const resolve = require('resolve'); 8 | 9 | /** 10 | * Get additional module paths based on the baseUrl of a compilerOptions object. 11 | * 12 | * @param {Object} options 13 | */ 14 | function getAdditionalModulePaths(options = {}) { 15 | const baseUrl = options.baseUrl; 16 | 17 | // We need to explicitly check for null and undefined (and not a falsy value) because 18 | // TypeScript treats an empty string as `.`. 19 | if (baseUrl == null) { 20 | // If there's no baseUrl set we respect NODE_PATH 21 | // Note that NODE_PATH is deprecated and will be removed 22 | // in the next major release of create-react-app. 23 | 24 | const nodePath = process.env.NODE_PATH || ''; 25 | return nodePath.split(path.delimiter).filter(Boolean); 26 | } 27 | 28 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 29 | 30 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is 31 | // the default behavior. 32 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { 33 | return null; 34 | } 35 | 36 | // Allow the user set the `baseUrl` to `appSrc`. 37 | if (path.relative(paths.appSrc, baseUrlResolved) === '') { 38 | return [paths.appSrc]; 39 | } 40 | 41 | // If the path is equal to the root directory we ignore it here. 42 | // We don't want to allow importing from the root directly as source files are 43 | // not transpiled outside of `src`. We do allow importing them with the 44 | // absolute path (e.g. `src/Components/Button.js`) but we set that up with 45 | // an alias. 46 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 47 | return null; 48 | } 49 | 50 | // Otherwise, throw an error. 51 | throw new Error( 52 | chalk.red.bold( 53 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." + 54 | ' Create React App does not support other values at this time.' 55 | ) 56 | ); 57 | } 58 | 59 | /** 60 | * Get webpack aliases based on the baseUrl of a compilerOptions object. 61 | * 62 | * @param {*} options 63 | */ 64 | function getWebpackAliases(options = {}) { 65 | const baseUrl = options.baseUrl; 66 | 67 | if (!baseUrl) { 68 | return {}; 69 | } 70 | 71 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 72 | 73 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 74 | return { 75 | src: paths.appSrc, 76 | }; 77 | } 78 | } 79 | 80 | /** 81 | * Get jest aliases based on the baseUrl of a compilerOptions object. 82 | * 83 | * @param {*} options 84 | */ 85 | function getJestAliases(options = {}) { 86 | const baseUrl = options.baseUrl; 87 | 88 | if (!baseUrl) { 89 | return {}; 90 | } 91 | 92 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 93 | 94 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 95 | return { 96 | '^src/(.*)$': '/src/$1', 97 | }; 98 | } 99 | } 100 | 101 | function getModules() { 102 | // Check if TypeScript is setup 103 | const hasTsConfig = fs.existsSync(paths.appTsConfig); 104 | const hasJsConfig = fs.existsSync(paths.appJsConfig); 105 | 106 | if (hasTsConfig && hasJsConfig) { 107 | throw new Error( 108 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' 109 | ); 110 | } 111 | 112 | let config; 113 | 114 | // If there's a tsconfig.json we assume it's a 115 | // TypeScript project and set up the config 116 | // based on tsconfig.json 117 | if (hasTsConfig) { 118 | const ts = require(resolve.sync('typescript', { 119 | basedir: paths.appNodeModules, 120 | })); 121 | config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; 122 | // Otherwise we'll check if there is jsconfig.json 123 | // for non TS projects. 124 | } else if (hasJsConfig) { 125 | config = require(paths.appJsConfig); 126 | } 127 | 128 | config = config || {}; 129 | const options = config.compilerOptions || {}; 130 | 131 | const additionalModulePaths = getAdditionalModulePaths(options); 132 | 133 | return { 134 | additionalModulePaths: additionalModulePaths, 135 | webpackAliases: getWebpackAliases(options), 136 | jestAliases: getJestAliases(options), 137 | hasTsConfig, 138 | }; 139 | } 140 | 141 | module.exports = getModules(); 142 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(inputPath, needsSlash) { 15 | const hasSlash = inputPath.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return inputPath.substr(0, inputPath.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${inputPath}/`; 20 | } else { 21 | return inputPath; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | How does your code sound? | SoundCode 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /public/ogg/bass/boffner1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/bass/boffner1.ogg -------------------------------------------------------------------------------- /public/ogg/bass/boffner2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/bass/boffner2.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-a2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a2.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-a2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a2.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-a3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a3.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-a3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a3.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-a4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a4.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-a4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a4.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-a5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a5.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-a5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-a5.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-c2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c2.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-c2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c2.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-c3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c3.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-c3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c3.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-c4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c4.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-c4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c4.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-c5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c5.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-c5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-c5.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds2.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds2.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds3.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds3.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds4.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds4.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds5.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-ds5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-ds5.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs2.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs2.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs3.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs3.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs4.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs4.wav -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs5.ogg -------------------------------------------------------------------------------- /public/ogg/cello/cello-fs5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/cello/cello-fs5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-a4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-a4.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-a4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-a4.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-a5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-a5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-a5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-a5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-as4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-as4.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-as4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-as4.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-as5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-as5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-as5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-as5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-b4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-b4.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-b4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-b4.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-b5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-b5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-b5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-b5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-c5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-c5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-c5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-c5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-c6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-c6.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-c6.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-c6.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-cs5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-cs5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-cs5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-cs5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-d5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-d5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-d5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-d5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-ds5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-ds5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-ds5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-ds5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-e5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-e5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-e5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-e5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-f5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-f5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-f5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-f5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-fs5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-fs5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-fs5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-fs5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-g4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-g4.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-g4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-g4.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-g5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-g5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-g5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-g5.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-gs4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-gs4.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-gs4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-gs4.wav -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-gs5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-gs5.ogg -------------------------------------------------------------------------------- /public/ogg/chorus/chorus-female-gs5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/chorus/chorus-female-gs5.wav -------------------------------------------------------------------------------- /public/ogg/drums/Clap.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/Clap.ogg -------------------------------------------------------------------------------- /public/ogg/drums/ClosedHat.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/ClosedHat.ogg -------------------------------------------------------------------------------- /public/ogg/drums/Kick.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/Kick.ogg -------------------------------------------------------------------------------- /public/ogg/drums/OpenHat.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/OpenHat.ogg -------------------------------------------------------------------------------- /public/ogg/drums/pedalD1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/pedalD1.ogg -------------------------------------------------------------------------------- /public/ogg/drums/pedalD2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/pedalD2.ogg -------------------------------------------------------------------------------- /public/ogg/drums/pedalU1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/pedalU1.ogg -------------------------------------------------------------------------------- /public/ogg/drums/pedalU2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/drums/pedalU2.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-a3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-a3.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-a3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-a3.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-a4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-a4.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-a4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-a4.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-a5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-a5.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-a5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-a5.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-c3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c3.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-c3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c3.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-c4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c4.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-c4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c4.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-c5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c5.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-c5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c5.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-c6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c6.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-c6.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-c6.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-ds3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-ds3.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-ds3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-ds3.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-ds4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-ds4.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-ds4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-ds4.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-ds5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-ds5.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-ds5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-ds5.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-fs3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-fs3.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-fs3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-fs3.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-fs4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-fs4.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-fs4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-fs4.wav -------------------------------------------------------------------------------- /public/ogg/flute/flute-fs5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-fs5.ogg -------------------------------------------------------------------------------- /public/ogg/flute/flute-fs5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/flute/flute-fs5.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-a2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a2.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-a2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a2.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-a3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a3.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-a3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a3.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-a4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a4.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-a4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a4.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-a5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a5.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-a5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a5.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-a6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a6.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-a6.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-a6.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-c2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c2.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-c2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c2.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-c3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c3.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-c3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c3.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-c4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c4.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-c4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c4.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-c5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c5.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-c5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c5.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-c6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c6.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-c6.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c6.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-c7.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c7.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-c7.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-c7.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds2.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds2.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds3.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds3.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds4.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds4.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds5.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds5.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds6.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-ds6.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-ds6.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs2.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs2.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs3.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs3.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs4.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs4.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs5.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs5.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs5.wav -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs6.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs6.ogg -------------------------------------------------------------------------------- /public/ogg/harp/harp-fs6.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/harp/harp-fs6.wav -------------------------------------------------------------------------------- /public/ogg/piano/A0v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A0v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/A1v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A1v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/A2v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A2v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/A3v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A3v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/A4v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A4v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/A5v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A5v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/A6v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A6v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/A7v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/A7v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C1v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C1v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C2v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C2v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C3v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C3v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C4v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C4v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C5v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C5v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C6v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C6v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C7v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C7v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/C8v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/C8v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Ds1v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Ds1v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Ds2v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Ds2v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Ds3v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Ds3v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Ds4v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Ds4v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Ds5v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Ds5v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Ds6v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Ds6v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Ds7v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Ds7v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Fs1v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Fs1v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Fs2v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Fs2v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Fs3v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Fs3v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Fs4v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Fs4v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Fs5v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Fs5v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Fs6v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Fs6v10.ogg -------------------------------------------------------------------------------- /public/ogg/piano/Fs7v10.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/piano/Fs7v10.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-as1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-as1.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-as1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-as1.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-as2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-as2.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-as2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-as2.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-as3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-as3.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-as3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-as3.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-cs2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-cs2.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-cs2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-cs2.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-cs3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-cs3.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-cs3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-cs3.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-cs4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-cs4.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-cs4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-cs4.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-e1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-e1.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-e1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-e1.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-e2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-e2.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-e2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-e2.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-e3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-e3.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-e3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-e3.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-g1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-g1.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-g1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-g1.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-g2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-g2.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-g2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-g2.wav -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-g3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-g3.ogg -------------------------------------------------------------------------------- /public/ogg/tuba-stc/tuba-stc-rr1-g3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/ogg/tuba-stc/tuba-stc-rr1-g3.wav -------------------------------------------------------------------------------- /public/play-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/play-bg.jpg -------------------------------------------------------------------------------- /public/preloader-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/preloader-bg.jpg -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#111111", 17 | "background_color": "#111111", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/social.png -------------------------------------------------------------------------------- /public/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/soundcode/8c0302324eb43574a9e4267935fd02fb818415dd/public/twitter.png -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'production'; 5 | process.env.NODE_ENV = 'production'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | 18 | const path = require('path'); 19 | const chalk = require('react-dev-utils/chalk'); 20 | const fs = require('fs-extra'); 21 | const webpack = require('webpack'); 22 | const configFactory = require('../config/webpack.config'); 23 | const paths = require('../config/paths'); 24 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 25 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); 26 | const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); 27 | const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); 28 | const printBuildError = require('react-dev-utils/printBuildError'); 29 | 30 | const measureFileSizesBeforeBuild = 31 | FileSizeReporter.measureFileSizesBeforeBuild; 32 | const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; 33 | const useYarn = fs.existsSync(paths.yarnLockFile); 34 | 35 | // These sizes are pretty large. We'll warn for bundles exceeding them. 36 | const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; 37 | const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; 38 | 39 | const isInteractive = process.stdout.isTTY; 40 | 41 | // Warn and crash if required files are missing 42 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 43 | process.exit(1); 44 | } 45 | 46 | // Generate configuration 47 | const config = configFactory('production'); 48 | 49 | // We require that you explicitly set browsers and do not fall back to 50 | // browserslist defaults. 51 | const { checkBrowsers } = require('react-dev-utils/browsersHelper'); 52 | checkBrowsers(paths.appPath, isInteractive) 53 | .then(() => { 54 | // First, read the current file sizes in build directory. 55 | // This lets us display how much they changed later. 56 | return measureFileSizesBeforeBuild(paths.appBuild); 57 | }) 58 | .then(previousFileSizes => { 59 | // Remove all content but keep the directory so that 60 | // if you're in it, you don't end up in Trash 61 | fs.emptyDirSync(paths.appBuild); 62 | // Merge with the public folder 63 | copyPublicFolder(); 64 | // Start the webpack build 65 | return build(previousFileSizes); 66 | }) 67 | .then( 68 | ({ stats, previousFileSizes, warnings }) => { 69 | if (warnings.length) { 70 | console.log(chalk.yellow('Compiled with warnings.\n')); 71 | console.log(warnings.join('\n\n')); 72 | console.log( 73 | '\nSearch for the ' + 74 | chalk.underline(chalk.yellow('keywords')) + 75 | ' to learn more about each warning.' 76 | ); 77 | console.log( 78 | 'To ignore, add ' + 79 | chalk.cyan('// eslint-disable-next-line') + 80 | ' to the line before.\n' 81 | ); 82 | } else { 83 | console.log(chalk.green('Compiled successfully.\n')); 84 | } 85 | 86 | console.log('File sizes after gzip:\n'); 87 | printFileSizesAfterBuild( 88 | stats, 89 | previousFileSizes, 90 | paths.appBuild, 91 | WARN_AFTER_BUNDLE_GZIP_SIZE, 92 | WARN_AFTER_CHUNK_GZIP_SIZE 93 | ); 94 | console.log(); 95 | 96 | const appPackage = require(paths.appPackageJson); 97 | const publicUrl = paths.publicUrl; 98 | const publicPath = config.output.publicPath; 99 | const buildFolder = path.relative(process.cwd(), paths.appBuild); 100 | printHostingInstructions( 101 | appPackage, 102 | publicUrl, 103 | publicPath, 104 | buildFolder, 105 | useYarn 106 | ); 107 | }, 108 | err => { 109 | const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; 110 | if (tscCompileOnError) { 111 | console.log( 112 | chalk.yellow( 113 | 'Compiled with the following type errors (you may want to check these before deploying your app):\n' 114 | ) 115 | ); 116 | printBuildError(err); 117 | } else { 118 | console.log(chalk.red('Failed to compile.\n')); 119 | printBuildError(err); 120 | process.exit(1); 121 | } 122 | } 123 | ) 124 | .catch(err => { 125 | if (err && err.message) { 126 | console.log(err.message); 127 | } 128 | process.exit(1); 129 | }); 130 | 131 | // Create the production build and print the deployment instructions. 132 | function build(previousFileSizes) { 133 | // We used to support resolving modules according to `NODE_PATH`. 134 | // This now has been deprecated in favor of jsconfig/tsconfig.json 135 | // This lets you use absolute paths in imports inside large monorepos: 136 | if (process.env.NODE_PATH) { 137 | console.log( 138 | chalk.yellow( 139 | 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.' 140 | ) 141 | ); 142 | console.log(); 143 | } 144 | 145 | console.log('Creating an optimized production build...'); 146 | 147 | const compiler = webpack(config); 148 | return new Promise((resolve, reject) => { 149 | compiler.run((err, stats) => { 150 | let messages; 151 | if (err) { 152 | if (!err.message) { 153 | return reject(err); 154 | } 155 | 156 | let errMessage = err.message; 157 | 158 | // Add additional information for postcss errors 159 | if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) { 160 | errMessage += 161 | '\nCompileError: Begins at CSS selector ' + 162 | err['postcssNode'].selector; 163 | } 164 | 165 | messages = formatWebpackMessages({ 166 | errors: [errMessage], 167 | warnings: [], 168 | }); 169 | } else { 170 | messages = formatWebpackMessages( 171 | stats.toJson({ all: false, warnings: true, errors: true }) 172 | ); 173 | } 174 | if (messages.errors.length) { 175 | // Only keep the first error. Others are often indicative 176 | // of the same problem, but confuse the reader with noise. 177 | if (messages.errors.length > 1) { 178 | messages.errors.length = 1; 179 | } 180 | return reject(new Error(messages.errors.join('\n\n'))); 181 | } 182 | if ( 183 | process.env.CI && 184 | (typeof process.env.CI !== 'string' || 185 | process.env.CI.toLowerCase() !== 'false') && 186 | messages.warnings.length 187 | ) { 188 | console.log( 189 | chalk.yellow( 190 | '\nTreating warnings as errors because process.env.CI = true.\n' + 191 | 'Most CI servers set it automatically.\n' 192 | ) 193 | ); 194 | return reject(new Error(messages.warnings.join('\n\n'))); 195 | } 196 | 197 | return resolve({ 198 | stats, 199 | previousFileSizes, 200 | warnings: messages.warnings, 201 | }); 202 | }); 203 | }); 204 | } 205 | 206 | function copyPublicFolder() { 207 | fs.copySync(paths.appPublic, paths.appBuild, { 208 | dereference: true, 209 | filter: file => file !== paths.appHtml, 210 | }); 211 | } 212 | -------------------------------------------------------------------------------- /scripts/start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'development'; 5 | process.env.NODE_ENV = 'development'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | 18 | const fs = require('fs'); 19 | const chalk = require('react-dev-utils/chalk'); 20 | const webpack = require('webpack'); 21 | const WebpackDevServer = require('webpack-dev-server'); 22 | const clearConsole = require('react-dev-utils/clearConsole'); 23 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 24 | const { 25 | choosePort, 26 | createCompiler, 27 | prepareProxy, 28 | prepareUrls, 29 | } = require('react-dev-utils/WebpackDevServerUtils'); 30 | const openBrowser = require('react-dev-utils/openBrowser'); 31 | const paths = require('../config/paths'); 32 | const configFactory = require('../config/webpack.config'); 33 | const createDevServerConfig = require('../config/webpackDevServer.config'); 34 | 35 | const useYarn = fs.existsSync(paths.yarnLockFile); 36 | const isInteractive = process.stdout.isTTY; 37 | 38 | // Warn and crash if required files are missing 39 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 40 | process.exit(1); 41 | } 42 | 43 | // Tools like Cloud9 rely on this. 44 | const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; 45 | const HOST = process.env.HOST || '0.0.0.0'; 46 | 47 | if (process.env.HOST) { 48 | console.log( 49 | chalk.cyan( 50 | `Attempting to bind to HOST environment variable: ${chalk.yellow( 51 | chalk.bold(process.env.HOST) 52 | )}` 53 | ) 54 | ); 55 | console.log( 56 | `If this was unintentional, check that you haven't mistakenly set it in your shell.` 57 | ); 58 | console.log( 59 | `Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}` 60 | ); 61 | console.log(); 62 | } 63 | 64 | // We require that you explicitly set browsers and do not fall back to 65 | // browserslist defaults. 66 | const { checkBrowsers } = require('react-dev-utils/browsersHelper'); 67 | checkBrowsers(paths.appPath, isInteractive) 68 | .then(() => { 69 | // We attempt to use the default port but if it is busy, we offer the user to 70 | // run on a different port. `choosePort()` Promise resolves to the next free port. 71 | return choosePort(HOST, DEFAULT_PORT); 72 | }) 73 | .then(port => { 74 | if (port == null) { 75 | // We have not found a port. 76 | return; 77 | } 78 | const config = configFactory('development'); 79 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; 80 | const appName = require(paths.appPackageJson).name; 81 | const useTypeScript = fs.existsSync(paths.appTsConfig); 82 | const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; 83 | const urls = prepareUrls(protocol, HOST, port); 84 | const devSocket = { 85 | warnings: warnings => 86 | devServer.sockWrite(devServer.sockets, 'warnings', warnings), 87 | errors: errors => 88 | devServer.sockWrite(devServer.sockets, 'errors', errors), 89 | }; 90 | // Create a webpack compiler that is configured with custom messages. 91 | const compiler = createCompiler({ 92 | appName, 93 | config, 94 | devSocket, 95 | urls, 96 | useYarn, 97 | useTypeScript, 98 | tscCompileOnError, 99 | webpack, 100 | }); 101 | // Load proxy config 102 | const proxySetting = require(paths.appPackageJson).proxy; 103 | const proxyConfig = prepareProxy(proxySetting, paths.appPublic); 104 | // Serve webpack assets generated by the compiler over a web server. 105 | const serverConfig = createDevServerConfig( 106 | proxyConfig, 107 | urls.lanUrlForConfig 108 | ); 109 | const devServer = new WebpackDevServer(compiler, serverConfig); 110 | // Launch WebpackDevServer. 111 | devServer.listen(port, HOST, err => { 112 | if (err) { 113 | return console.log(err); 114 | } 115 | if (isInteractive) { 116 | clearConsole(); 117 | } 118 | 119 | // We used to support resolving modules according to `NODE_PATH`. 120 | // This now has been deprecated in favor of jsconfig/tsconfig.json 121 | // This lets you use absolute paths in imports inside large monorepos: 122 | if (process.env.NODE_PATH) { 123 | console.log( 124 | chalk.yellow( 125 | 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.' 126 | ) 127 | ); 128 | console.log(); 129 | } 130 | 131 | console.log(chalk.cyan('Starting the development server...\n')); 132 | openBrowser(urls.localUrlForBrowser); 133 | }); 134 | 135 | ['SIGINT', 'SIGTERM'].forEach(function(sig) { 136 | process.on(sig, function() { 137 | devServer.close(); 138 | process.exit(); 139 | }); 140 | }); 141 | }) 142 | .catch(err => { 143 | if (err && err.message) { 144 | console.log(err.message); 145 | } 146 | process.exit(1); 147 | }); 148 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | 19 | const jest = require('jest'); 20 | const execSync = require('child_process').execSync; 21 | let argv = process.argv.slice(2); 22 | 23 | function isInGitRepository() { 24 | try { 25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); 26 | return true; 27 | } catch (e) { 28 | return false; 29 | } 30 | } 31 | 32 | function isInMercurialRepository() { 33 | try { 34 | execSync('hg --cwd . root', { stdio: 'ignore' }); 35 | return true; 36 | } catch (e) { 37 | return false; 38 | } 39 | } 40 | 41 | // Watch unless on CI or explicitly running all tests 42 | if ( 43 | !process.env.CI && 44 | argv.indexOf('--watchAll') === -1 && 45 | argv.indexOf('--watchAll=false') === -1 46 | ) { 47 | // https://github.com/facebook/create-react-app/issues/5210 48 | const hasSourceControl = isInGitRepository() || isInMercurialRepository(); 49 | argv.push(hasSourceControl ? '--watch' : '--watchAll'); 50 | } 51 | 52 | 53 | jest.run(argv); 54 | -------------------------------------------------------------------------------- /src/classes/AudioRecorder.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | 3 | class AudioRecorder { 4 | private context: AudioContext; 5 | private recorder: MediaRecorder; 6 | private chunks: Blob[] = []; 7 | 8 | destination: MediaStreamAudioDestinationNode; 9 | 10 | constructor() { 11 | if (typeof MediaRecorder !== "undefined") { 12 | this.context = Tone.context; 13 | this.destination = this.context.createMediaStreamDestination(); 14 | this.recorder = new MediaRecorder(this.destination.stream); 15 | 16 | this.recorder.ondataavailable = this.handleDataAvailable.bind(this); 17 | } 18 | } 19 | 20 | public isAvailable() { 21 | return typeof MediaRecorder !== "undefined"; 22 | } 23 | 24 | private handleDataAvailable(event: BlobEvent) { 25 | this.chunks.push(event.data); 26 | } 27 | 28 | public start() { 29 | if (typeof MediaRecorder !== "undefined") { 30 | this.clear(); 31 | this.recorder.start(); 32 | } 33 | } 34 | 35 | public stop() { 36 | if (typeof MediaRecorder !== "undefined") { 37 | if (this.recorder.state === "recording") this.recorder.stop(); 38 | } 39 | } 40 | 41 | public getAudioRecordBlob() { 42 | return new Blob(this.chunks, { type: "audio/ogg; codecs=opus" }); 43 | } 44 | 45 | public clear() { 46 | this.chunks = []; 47 | } 48 | } 49 | 50 | export default new AudioRecorder(); 51 | -------------------------------------------------------------------------------- /src/classes/Conductor.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import ts from "typescript"; 3 | import bass from "../instruments/bass"; 4 | import cello from "../instruments/cello"; 5 | import chorusFemale from "../instruments/chorusFemale"; 6 | import drums from "../instruments/drums"; 7 | import flute from "../instruments/flute"; 8 | import harp from "../instruments/harp"; 9 | import piano from "../instruments/piano"; 10 | import tubaSTC from "../instruments/tuba-stc"; 11 | import createNotesPlayer, { INotesPlayer } from "../players/createNotesPlayer"; 12 | import createTimeNotesPlayer, { 13 | ITimeNotesPlayer 14 | } from "../players/createTimeNotesPlayer"; 15 | import AudioRecorder from "./AudioRecorder"; 16 | import Orchestra from "./Orchestra"; 17 | 18 | interface ICallbacks { 19 | end: (() => any)[]; 20 | notify: ((text: string) => any)[]; 21 | } 22 | 23 | class Conductor { 24 | orchestra: Orchestra; 25 | beatLoop: ITimeNotesPlayer; 26 | chordsLoop: INotesPlayer; 27 | pianoArtist: INotesPlayer; 28 | celloArtist: INotesPlayer; 29 | harpArtist: ITimeNotesPlayer; 30 | fluteArtist: ITimeNotesPlayer; 31 | tubaSTCArtist: INotesPlayer; 32 | chorusFemaleArtist: ITimeNotesPlayer; 33 | hatArtist: ITimeNotesPlayer; 34 | bassLoop: ITimeNotesPlayer; 35 | 36 | private callbacks: ICallbacks = { 37 | end: [], 38 | notify: [] 39 | }; 40 | 41 | constructor(orchestra: Orchestra) { 42 | this.orchestra = orchestra; 43 | this.beatLoop = this.createBeatLoop(); 44 | this.chordsLoop = this.createChordsLoop(); 45 | this.bassLoop = this.createBassLoop(); 46 | this.hatArtist = this.createHatArtist(); 47 | this.pianoArtist = this.createPianoArtist(); 48 | this.celloArtist = this.createCelloArtist(); 49 | this.harpArtist = this.createHarpArtist(); 50 | this.fluteArtist = this.createFluteArtist(); 51 | this.tubaSTCArtist = this.createTubaSTCArtist(); 52 | this.chorusFemaleArtist = this.createChorusFemaleArtist(); 53 | 54 | this.beatLoop.start(); 55 | this.chordsLoop.start(); 56 | this.bassLoop.start(); 57 | this.hatArtist.start(); 58 | this.pianoArtist.start(); 59 | this.celloArtist.start(); 60 | this.harpArtist.start(); 61 | this.fluteArtist.start(); 62 | this.tubaSTCArtist.start(); 63 | this.chorusFemaleArtist.start(); 64 | 65 | this.notify = this.notify.bind(this); 66 | this.handlePianoEnd = this.handlePianoEnd.bind(this); 67 | } 68 | 69 | public getDurationInSeconds() { 70 | const bars = this.orchestra.getBarsCount(); 71 | const beats = bars * 4; 72 | const bpm = Tone.Transport.bpm.value; 73 | return (beats / bpm) * 60; 74 | } 75 | 76 | public play() { 77 | this.pianoArtist.on("end", this.handlePianoEnd); 78 | this.pianoArtist.on("note", this.notify); 79 | this.harpArtist.on("note", this.notify); 80 | this.chorusFemaleArtist.on("note", this.notify); 81 | this.hatArtist.on("note", this.notify); 82 | 83 | AudioRecorder.start(); 84 | this.harpArtist.instrument.it().volume.value -= 2; 85 | this.tubaSTCArtist.instrument.it().volume.value -= 2; 86 | Tone.Transport.start(); 87 | } 88 | 89 | public stop() { 90 | this.pianoArtist.off("end", this.handlePianoEnd); 91 | this.pianoArtist.off("note", this.notify); 92 | this.harpArtist.off("note", this.notify); 93 | this.chorusFemaleArtist.off("note", this.notify); 94 | this.hatArtist.off("note", this.notify); 95 | 96 | AudioRecorder.stop(); 97 | Tone.Transport.stop(); 98 | } 99 | 100 | public on(type: T, callback: ICallbacks[T][0]) { 101 | if (!this.callbacks[type].includes(callback as any)) { 102 | this.callbacks[type].push(callback as any); 103 | } 104 | } 105 | 106 | public off(type: T, callback: ICallbacks[T][0]) { 107 | this.callbacks[type] = this.callbacks[type].filter( 108 | c => c !== callback 109 | ) as any; 110 | } 111 | 112 | private notify( 113 | instrumentName: string, 114 | note: string | string[], 115 | node: ts.Node | null 116 | ) { 117 | if (node) { 118 | const text = this.orchestra.source.substring(node.pos, node.end).trim(); 119 | for (const callback of this.callbacks.notify) { 120 | callback(text); 121 | } 122 | } 123 | } 124 | 125 | public destroy() { 126 | this.callbacks = { end: [], notify: [] }; 127 | this.beatLoop.destroy(); 128 | this.chordsLoop.destroy(); 129 | this.bassLoop.destroy(); 130 | this.hatArtist.destroy(); 131 | this.pianoArtist.destroy(); 132 | this.celloArtist.destroy(); 133 | this.harpArtist.destroy(); 134 | this.fluteArtist.destroy(); 135 | this.tubaSTCArtist.destroy(); 136 | this.chorusFemaleArtist.destroy(); 137 | Tone.Transport.stop(); 138 | Tone.Transport.cancel(); 139 | } 140 | 141 | private handlePianoEnd() { 142 | setTimeout(() => { 143 | this.stop(); 144 | for (const callback of this.callbacks["end"]) { 145 | callback(); 146 | } 147 | }, 2000); 148 | } 149 | 150 | private createBeatLoop() { 151 | return createTimeNotesPlayer(drums, this.orchestra.getBeatITNotes()); 152 | } 153 | 154 | private createChordsLoop() { 155 | return createNotesPlayer(piano, this.orchestra.getChordsINotes(), true); 156 | } 157 | 158 | private createPianoArtist() { 159 | return createNotesPlayer(piano, this.orchestra.getMelodyINotes()); 160 | } 161 | 162 | private createCelloArtist() { 163 | return createNotesPlayer(cello, this.orchestra.getCelloINotes()); 164 | } 165 | 166 | private createHarpArtist() { 167 | return createTimeNotesPlayer(harp, this.orchestra.getHarpITNotes()); 168 | } 169 | 170 | private createFluteArtist() { 171 | return createTimeNotesPlayer(flute, this.orchestra.getFluteITNotes()); 172 | } 173 | 174 | private createTubaSTCArtist() { 175 | return createNotesPlayer(tubaSTC, this.orchestra.getTubaSTCINotes()); 176 | } 177 | 178 | private createChorusFemaleArtist() { 179 | return createTimeNotesPlayer( 180 | chorusFemale, 181 | this.orchestra.getFemaleChorusITNotes() 182 | ); 183 | } 184 | 185 | private createHatArtist() { 186 | return createTimeNotesPlayer(drums, this.orchestra.getHatITNotes()); 187 | } 188 | 189 | private createBassLoop() { 190 | return createTimeNotesPlayer(bass, this.orchestra.getBassITNotes()); 191 | } 192 | } 193 | 194 | export default Conductor; 195 | -------------------------------------------------------------------------------- /src/classes/Orchestra.ts: -------------------------------------------------------------------------------- 1 | import { chord as parseChord } from "@tonaljs/chord"; 2 | import { 3 | majorKey as parseMajorKey, 4 | minorKey as parseMinorKey 5 | } from "@tonaljs/key"; 6 | import { note as parseNote } from "@tonaljs/tonal"; 7 | import * as esprima from "esprima"; 8 | import { max, min } from "lodash"; 9 | import ts from "typescript"; 10 | import isDebug from "../isDebug"; 11 | import { INote, ITimeNote } from "../types"; 12 | import { mapPower2, mapRange, sumOfP2 } from "../utils"; 13 | import calculateMood from "../utils/calculateMood"; 14 | import calculateProgression from "../utils/calculateProgression"; 15 | import createArpeggio, { 16 | calculateArpeggioIndex 17 | } from "../utils/createArpeggio"; 18 | import getChordHarmonic from "../utils/getChordHarmonic"; 19 | import roman from "../utils/roman"; 20 | 21 | const collectAllNodes = (nodes: ts.Node[], result: ts.Node[] = []) => { 22 | for (const node of nodes) { 23 | collectAllNodes(node.getChildren(), result); 24 | result.push(node); 25 | } 26 | }; 27 | 28 | interface INotePosition { 29 | bar: number; 30 | positionInBar: number; 31 | inote: INote; 32 | } 33 | 34 | type Bar = INotePosition[]; 35 | 36 | interface INodePosition { 37 | bar: number; 38 | positionInBar: number; 39 | node: ts.Node; 40 | } 41 | 42 | class Orchestra { 43 | nodes: ts.Node[] = []; 44 | inodes: INodePosition[] = []; 45 | minIdentifierWeight: number; 46 | maxIdentifierWeight: number; 47 | bars: Bar[] = [[]]; 48 | chords: string[][]; 49 | minSpecialWeight: number; 50 | maxSpecialWeight: number; 51 | source: string; 52 | scale: string; 53 | progression: string[]; 54 | arpeggioIndex: number; 55 | constructor(source: string) { 56 | this.source = source; 57 | this.getNodes(); 58 | this.scale = this.getScale(); 59 | if (isDebug) { 60 | console.log("SCALE", this.scale); 61 | } 62 | this.progression = this.getProgression(); 63 | if (isDebug) { 64 | console.log("PROGRESSION", this.progression); 65 | } 66 | this.arpeggioIndex = this.getArpeggio(); 67 | if (isDebug) { 68 | console.log("ARPEGGIO INDEX", this.arpeggioIndex); 69 | } 70 | this.chords = this.getChords(); 71 | this.createIdentifierWeights(); 72 | this.createSpecialWeights(); 73 | this.collectMelodyBars(); 74 | } 75 | 76 | public static validateSource(source: string) { 77 | const { outputText } = ts.transpileModule(source, { 78 | compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.React } 79 | }); 80 | esprima.parseScript(outputText, { jsx: true }); 81 | } 82 | 83 | private getNodes() { 84 | const sc = ts.createSourceFile( 85 | "sound.tsx", 86 | this.source, 87 | ts.ScriptTarget.Latest, 88 | true 89 | ); 90 | const sourceFileNode = sc.getChildren()[0]; 91 | collectAllNodes(sourceFileNode.getChildren(), this.nodes); 92 | } 93 | 94 | getBarsCount() { 95 | return this.bars.length; 96 | } 97 | 98 | getFemaleChorusITNotes() { 99 | const groupedByBar: { 100 | [bar: number]: { node: ts.Node; weight: number }[]; 101 | } = {}; 102 | for (const inode of this.inodes) { 103 | if (ts.isStringLiteral(inode.node)) { 104 | if (!groupedByBar[inode.bar]) groupedByBar[inode.bar] = []; 105 | const text = inode.node.getText(); 106 | const weight = this.getTextWeight(text); 107 | groupedByBar[inode.bar].push({ node: inode.node, weight }); 108 | } 109 | } 110 | const itnotes: ITimeNote[] = []; 111 | for (const bar in groupedByBar) { 112 | const weight = 113 | groupedByBar[bar].reduce((acc, { weight }) => acc + weight, 0) / 114 | groupedByBar[bar].length; 115 | const chordNotes = this.getBarChord(parseInt(bar)); 116 | const nodeIndex = Math.round( 117 | mapRange( 118 | weight, 119 | this.minIdentifierWeight, 120 | this.maxIdentifierWeight, 121 | 0, 122 | chordNotes.length - 1 123 | ) 124 | ); 125 | const note = chordNotes[nodeIndex]; 126 | itnotes.push({ 127 | bar: parseInt(bar), 128 | beat: 0, 129 | note: parseNote(note).pc + 3, 130 | duration: 1, 131 | velocity: 0.2, 132 | display: groupedByBar[bar][0].node 133 | }); 134 | } 135 | return itnotes; 136 | } 137 | 138 | getHatITNotes() { 139 | const notes: ITimeNote[] = []; 140 | for (const inode of this.inodes) { 141 | if (ts.isNumericLiteral(inode.node)) { 142 | const text = inode.node.getText(); 143 | const item = Math.round(parseFloat(text)); 144 | notes.push({ 145 | note: item % 2 === 0 ? "openhat" : "closedhat", 146 | bar: inode.bar, 147 | beat: inode.positionInBar, 148 | play: (instrument, note, duration, time) => { 149 | instrument 150 | .it() 151 | .get(note) 152 | .start(time); 153 | }, 154 | duration: 8, 155 | velocity: 0.8, 156 | display: inode.node 157 | }); 158 | } 159 | } 160 | return notes; 161 | } 162 | 163 | getBassITNotes() { 164 | const notes: ITimeNote[] = []; 165 | const speed = 8; 166 | for (let bar = 0; bar < this.bars.length; bar++) { 167 | const chordNotes = this.getBarChord(bar); 168 | for (let i = 0; i < speed; i++) { 169 | const beat = i / speed; 170 | const note = chordNotes[i % chordNotes.length]; 171 | notes.push({ 172 | note: parseNote(note).pc + 3, 173 | duration: speed, 174 | bar: bar, 175 | beat: beat, 176 | velocity: 0.2, 177 | display: null 178 | }); 179 | } 180 | } 181 | return notes; 182 | } 183 | 184 | getBeatITNotes() { 185 | const notes: ITimeNote[] = []; 186 | for (let i = 0; i < 4; i++) { 187 | const beat = i / 4; 188 | notes.push({ 189 | note: "kick", 190 | duration: 4, 191 | play: (instrument, note, duration, time) => { 192 | instrument 193 | .it() 194 | .get(note) 195 | .start(time); 196 | }, 197 | beat, 198 | display: null 199 | }); 200 | if (i % 2 === 1) { 201 | notes.push({ 202 | note: "clap", 203 | duration: 4, 204 | play: (instrument, note, duration, time) => { 205 | instrument 206 | .it() 207 | .get(note) 208 | .start(time); 209 | }, 210 | beat, 211 | display: null 212 | }); 213 | } 214 | } 215 | 216 | return notes; 217 | } 218 | 219 | getChordsINotes() { 220 | const notes: INote[] = []; 221 | for (const chord of this.chords) { 222 | const chordNotes = createArpeggio(chord, this.arpeggioIndex); 223 | notes.push(...chordNotes); 224 | } 225 | return notes; 226 | } 227 | 228 | getMelodyINotes() { 229 | return this.bars.reduce((acc, bar) => { 230 | acc.push(...bar.map(({ inote }) => inote)); 231 | return acc; 232 | }, [] as INote[]); 233 | } 234 | 235 | getCelloINotes() { 236 | const notes: INote[] = []; 237 | for (let bar = 0; bar < this.bars.length; bar++) { 238 | const chord = this.chords[bar % this.chords.length]; 239 | const bassNote = chord[0]; 240 | notes.push({ note: bassNote, duration: 1, velocity: 0.3, display: null }); 241 | } 242 | return notes; 243 | } 244 | 245 | getFluteITNotes() { 246 | const itnotes: ITimeNote[] = []; 247 | for (const inode of this.inodes) { 248 | if (ts.isIdentifier(inode.node)) { 249 | const text = inode.node.getText(); 250 | const chord = this.getBarChord(inode.bar); 251 | const chordNotes = getChordHarmonic(chord); 252 | const weight = this.getTextWeight(text); 253 | const nodeIndex = Math.round( 254 | mapRange( 255 | weight, 256 | this.minIdentifierWeight, 257 | this.maxIdentifierWeight, 258 | 0, 259 | chordNotes.length - 1 260 | ) 261 | ); 262 | const note = chordNotes[nodeIndex]; 263 | itnotes.push({ 264 | bar: inode.bar, 265 | beat: inode.positionInBar, 266 | note: parseNote(note).pc + 5, 267 | duration: 4, 268 | velocity: 0.1, 269 | display: inode.node 270 | }); 271 | } 272 | } 273 | return itnotes; 274 | } 275 | 276 | getTubaSTCINotes() { 277 | const inotes: INote[] = []; 278 | const melodyNotes = this.getMelodyINotes(); 279 | for (const note of melodyNotes) { 280 | if (note.note !== "r" && note.duration > 4) { 281 | inotes.push({ ...note, sustain: note.duration, velocity: 0.5 }); 282 | } else { 283 | inotes.push({ ...note, note: "r" }); 284 | } 285 | } 286 | return inotes; 287 | } 288 | 289 | getHarpITNotes() { 290 | const itnotes: ITimeNote[] = []; 291 | for (const inode of this.inodes) { 292 | if (this.isSpecialCharacter(inode.node)) { 293 | const text = inode.node.getText(); 294 | const index = text.charCodeAt(0); 295 | const bar = inode.bar; 296 | const beat = inode.positionInBar; 297 | if (!isNaN(index)) { 298 | const chord = this.chords[bar % this.chords.length]; 299 | const notes = getChordHarmonic(chord); 300 | const nodeIndex = Math.round( 301 | mapRange( 302 | index, 303 | this.minSpecialWeight, 304 | this.maxSpecialWeight, 305 | 0, 306 | notes.length - 1 307 | ) 308 | ); 309 | const note = notes[nodeIndex]; 310 | itnotes.push({ 311 | note, 312 | bar, 313 | beat, 314 | duration: 16, 315 | velocity: 0.6, 316 | display: inode.node 317 | }); 318 | } 319 | } 320 | } 321 | return itnotes; 322 | } 323 | 324 | private getBarChord(bar: number) { 325 | return this.chords[bar % this.chords.length]; 326 | } 327 | 328 | private getTextWeight(text) { 329 | return text.split("").reduce((acc, t) => acc + t.charCodeAt(0), 0); 330 | } 331 | 332 | private createIdentifierWeights() { 333 | const identifierWeights: number[] = []; 334 | for (const node of this.nodes) { 335 | if (ts.isToken(node)) { 336 | if (!this.isSpecialCharacter(node)) { 337 | const value = node 338 | .getText() 339 | .split("") 340 | .reduce((acc, t) => acc + t.charCodeAt(0), 0); 341 | identifierWeights.push(value); 342 | } 343 | } 344 | } 345 | 346 | this.minIdentifierWeight = min(identifierWeights) as number; 347 | this.maxIdentifierWeight = max(identifierWeights) as number; 348 | } 349 | 350 | private createSpecialWeights() { 351 | const specialWeights: number[] = []; 352 | for (const node of this.nodes) { 353 | if (this.isSpecialCharacter(node)) { 354 | const text = node.getText(); 355 | const index = text.charCodeAt(0); 356 | if (!isNaN(index)) { 357 | specialWeights.push(index); 358 | } 359 | } 360 | } 361 | this.minSpecialWeight = min(specialWeights) as number; 362 | this.maxSpecialWeight = max(specialWeights) as number; 363 | } 364 | 365 | private isSpecialCharacter(node: ts.Node) { 366 | const text = node.getText(); 367 | return /^[,.;:[\]{}()*=<>/]|(>=)$/.test(text) || text.length === 0; 368 | } 369 | 370 | /** Count bars by melody, and assign bar, position to nodes */ 371 | private collectMelodyBars() { 372 | let bar = 0; 373 | let bar_fill = 1; 374 | for (const node of this.nodes) { 375 | let inote: INote; 376 | if (ts.isToken(node)) { 377 | this.inodes.push({ bar, positionInBar: 1 - bar_fill, node }); 378 | if (this.isSpecialCharacter(node)) { 379 | inote = { note: "r", duration: 8, display: node }; 380 | } else { 381 | const text = node.getText(); 382 | const length = text.length; 383 | const chord = this.chords[bar % this.chords.length]; 384 | const chordNotes = getChordHarmonic(chord); 385 | const weight = text 386 | .split("") 387 | .reduce((acc, t) => acc + t.charCodeAt(0), 0); 388 | const nodeIndex = Math.round( 389 | mapRange( 390 | weight, 391 | this.minIdentifierWeight, 392 | this.maxIdentifierWeight, 393 | 0, 394 | chordNotes.length - 1 395 | ) 396 | ); 397 | const note = chordNotes[nodeIndex]; 398 | const duration = mapPower2(Math.min(10, length), 10, 1, 2, 16); 399 | inote = { note, duration, sustain: 2, display: node }; 400 | } 401 | const bar_part = 1 / inote.duration; 402 | if (bar_fill - bar_part >= 0) { 403 | this.bars[bar].push({ 404 | positionInBar: 1 - bar_fill, 405 | inote, 406 | bar 407 | }); 408 | bar_fill -= bar_part; 409 | } else { 410 | const durationsLeft = sumOfP2(bar_fill); 411 | for (const durationLeftFac of durationsLeft) { 412 | this.bars[bar].push({ 413 | positionInBar: 1 - durationLeftFac, 414 | inote: { 415 | note: "r", 416 | duration: 1 / durationLeftFac, 417 | display: null 418 | }, 419 | bar 420 | }); 421 | } 422 | bar_fill = 0; 423 | } 424 | } 425 | 426 | if (bar_fill === 0) { 427 | bar_fill = 1; 428 | bar++; 429 | this.bars.push([]); 430 | } 431 | } 432 | } 433 | 434 | private getScale() { 435 | const mood = calculateMood(this.source); 436 | return mood; 437 | } 438 | 439 | private getProgression() { 440 | const progression = calculateProgression(this.nodes); 441 | return progression; 442 | } 443 | 444 | private getChords() { 445 | const chordOctave: number = 3; 446 | const chords: string[][] = []; 447 | const [keyRoot, quality] = this.scale.split(" "); 448 | const tonic = parseNote(keyRoot).pc; 449 | let keyChords: string[]; 450 | if (quality === "major") { 451 | const key = parseMajorKey(tonic); 452 | keyChords = key.chords; 453 | } else { 454 | const key = parseMinorKey(tonic); 455 | keyChords = key.natural.chords; 456 | } 457 | for (const p of this.progression) { 458 | const i = roman(p.toUpperCase()); 459 | const chord = keyChords[i - 1]; 460 | const chordNotes = parseChord(chord).notes.map( 461 | root => parseNote(root).pc + chordOctave 462 | ); 463 | // Remove diminished chord 464 | chords.push(chordNotes.slice(0, 3)); 465 | } 466 | return chords; 467 | } 468 | 469 | private getArpeggio() { 470 | return calculateArpeggioIndex(this.nodes); 471 | } 472 | } 473 | 474 | export default Orchestra; 475 | -------------------------------------------------------------------------------- /src/components/About.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container } from "react-bootstrap"; 3 | import styled from "styled-components"; 4 | 5 | interface IProps {} 6 | 7 | const About: React.SFC = ({}) => { 8 | return ( 9 | 10 | 11 |
12 |

About the idea

13 |
14 |

15 | As a developer, I love listening to music while coding. The orchestral 16 | music allows me to focus more on what I do. And one day I noticed my 17 | fingers dance on the keyboard by the music rhythm. Like playing the 18 | piano. Every word or symbol in the code was written with harmony. And 19 | then I thought... how it could sound... The code I write every day? 20 |

21 |

22 | And the idea was born. 23 |

24 |

How it works

25 |

26 | Firstly, we load the sound fonts of the instruments which are used in 27 | this little orchestra. When you paste or write your code (or using our 28 | example), we parse it using the TypeScript AST parser to individual 29 | nodes. Then the composition begins. 30 |

31 |

The mood of the code

32 |

33 | By code source, we determine the mood of the code. The more cheerful 34 | words in the code, the happier the mood and vice versa. The mood of 35 | the code is used to set the musical scale. If happy, a Major will be 36 | likely selected, if sad - Minor. 37 |

38 |

Chords

39 |

40 | By the code source and with some easy math we choose which chords 41 | progression play from the determined musical scale. 42 |

43 |

The Melody of the piano

44 |

45 | Each piano note is the TypeScript Token. With some math, we set the 46 | note, pitch, duration and time when to play. The special symbols like 47 | ,.+-/* and etc are excluded and used in the other instrument 48 |

49 |

Other instruments

50 |

51 | Each instrument has its own notes. Some just looping the notes of the 52 | chord, while others play specific notes by the source code. For 53 | example, the Cello always plays active chord root note, when Harp only 54 | plays at the special characters or Chorus at the strings. 55 |

56 |

Source Code

57 |

58 | This project is open source, you can check the source code on the 59 | GitHub:{" "} 60 | 61 | 62 | epranka/soundcode 63 | 64 | 65 | . 66 |

67 |

68 | Have ideas on how to improve it? New features? Feel free to share it 69 | on the{" "} 70 | 71 | 72 | GitHub Issues 73 | 74 | 75 | . 76 |

77 |
78 |
79 | ); 80 | }; 81 | 82 | const AboutStyled = styled.div` 83 | position: relative; 84 | z-index: 1; 85 | color: #eee; 86 | 87 | .container { 88 | max-width: 600px; 89 | } 90 | 91 | p a { 92 | text-decoration: none; 93 | color: white; 94 | &:hover { 95 | color: white; 96 | } 97 | } 98 | `; 99 | 100 | const Header = styled.div` 101 | text-align: center; 102 | margin-bottom: 2rem; 103 | `; 104 | 105 | export default About; 106 | -------------------------------------------------------------------------------- /src/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import CodePage from "./CodePage"; 3 | import { OrchestraContextProvider } from "./OrchestraContext"; 4 | import PlayPage from "./PlayPage"; 5 | import Preloader from "./Preloader"; 6 | import Credits from "./Credits"; 7 | 8 | const App: React.FC = () => { 9 | const [showPreloader, setShowPreloader] = useState(true); 10 | const [showPlayPage, setShowPlayPage] = useState(false); 11 | 12 | return ( 13 | 14 | setShowPlayPage(true)} /> 15 | setShowPreloader(false)} /> 16 | setShowPlayPage(false)} 19 | /> 20 | 21 | 22 | ); 23 | }; 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /src/components/CodeEditor.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import Editor, { monaco } from "@monaco-editor/react"; 3 | 4 | interface IProps { 5 | width: number; 6 | height: number; 7 | value: string; 8 | onChange: (value: string) => any; 9 | } 10 | 11 | const CodeEditor: React.SFC = ({ value, onChange, width, height }) => { 12 | const handleEditorDidMount = (_, editor) => { 13 | editor.onDidChangeModelContent(_ => { 14 | onChange(editor.getValue()); 15 | }); 16 | }; 17 | 18 | useEffect(() => { 19 | monaco.init().then(monaco => { 20 | monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ 21 | noSemanticValidation: true, 22 | noSyntaxValidation: true, 23 | noSuggestionDiagnostics: true 24 | }); 25 | }); 26 | }, []); 27 | 28 | return ( 29 | 57 | ); 58 | }; 59 | 60 | export default CodeEditor; 61 | -------------------------------------------------------------------------------- /src/components/CodePage.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useContext } from "react"; 2 | import styled from "styled-components"; 3 | import defaultSource from "../defaultSource"; 4 | import { useWindowSize } from "react-use"; 5 | import { Container, Row, Col } from "react-bootstrap"; 6 | import CodeEditor from "./CodeEditor"; 7 | import OrchestraContext from "./OrchestraContext"; 8 | import About from "./About"; 9 | 10 | interface IProps { 11 | onCreateOrchestra: () => any; 12 | } 13 | 14 | const CodePage: React.SFC = ({ onCreateOrchestra }) => { 15 | const [source, setSource] = useState(defaultSource); 16 | const [{ estimatedTime }, updateFromSource] = useContext(OrchestraContext); 17 | 18 | const { width, height } = useWindowSize(); 19 | let editorWidth = Math.min(width, 800) - 40; 20 | let editorHeight = Math.max(height * 0.5, 300); 21 | 22 | useEffect(() => { 23 | updateFromSource(source); 24 | }, [source]); 25 | 26 | return ( 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

Enter your Orchestra code

36 | 37 | This project is created for the javascript{" "} 38 | code (TS is supported), but you can try anything 39 | 40 |
41 | 42 |
43 |
44 | 45 | 51 | setSource("")}>Clear 52 | 53 | 54 | 55 | 56 | 57 | 58 | 𝄞 59 | Estimated time: {estimatedTime} s 60 | 61 |
62 | 63 | Create the Little orchestra 64 | 65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 |
73 |
74 | ); 75 | }; 76 | 77 | const Header = styled.div` 78 | color: #eee; 79 | z-index: 2; 80 | position: relative; 81 | max-width: 320px; 82 | margin: 3rem auto; 83 | margin-bottom: 2rem; 84 | 85 | h1 { 86 | display: block; 87 | text-align: center; 88 | margin-bottom: 1rem; 89 | } 90 | 91 | @media screen and (min-width: 576px) { 92 | margin: 6rem auto; 93 | margin-bottom: 5rem; 94 | h1 { 95 | font-size: 3rem; 96 | } 97 | } 98 | `; 99 | 100 | const Hint = styled.p` 101 | text-align: center; 102 | `; 103 | 104 | const EditorContainer = styled.div` 105 | position: relative; 106 | margin: 0 auto; 107 | padding: 20px; 108 | background: #202124; 109 | border: 2px solid #9e9e9e; 110 | border-radius: 0.125rem; 111 | `; 112 | 113 | const ClearButton = styled.div` 114 | z-index: 1; 115 | position: absolute; 116 | right: -2px; 117 | bottom: -33px; 118 | color: white; 119 | border: 2px solid #9e9e9e; 120 | border-radius: 0.125rem; 121 | padding: 5px 17px; 122 | text-align: center; 123 | font-size: 0.8rem; 124 | cursor: pointer; 125 | 126 | transition: background 0.25s ease; 127 | user-select: none; 128 | 129 | &:hover { 130 | background: rgba(32, 33, 36, 0.8); 131 | } 132 | `; 133 | 134 | const PlayControlContainer = styled.div` 135 | text-align: center; 136 | color: #eee; 137 | margin: 4rem auto; 138 | margin-bottom: 6rem; 139 | `; 140 | 141 | const EstimatedTime = styled.div` 142 | font-size: 1.25rem; 143 | margin-bottom: 1rem; 144 | `; 145 | 146 | const TrebleClef = styled.span` 147 | margin-right: 10px; 148 | `; 149 | 150 | const PlayButton = styled.div` 151 | display: inline-block; 152 | text-align: center; 153 | font-size: 1.25rem; 154 | border: 2px solid #9e9e9e; 155 | border-radius: 0.125rem; 156 | padding: 10px 30px; 157 | cursor: pointer; 158 | 159 | transition: background 0.25s ease; 160 | user-select: none; 161 | 162 | &:hover { 163 | background: rgba(32, 33, 36, 0.8); 164 | } 165 | `; 166 | 167 | const CodeEditorStyled = styled.div` 168 | position: fixed; 169 | top: 0; 170 | left: 0; 171 | right: 0; 172 | bottom: 0; 173 | background-image: url("/code-bg.jpg"); 174 | background-size: cover; 175 | background-position: center center; 176 | `; 177 | 178 | const ScrollableContainer = styled.div` 179 | overflow-y: auto; 180 | position: absolute; 181 | left: 0; 182 | right: 0; 183 | top: 0; 184 | bottom: 0; 185 | `; 186 | 187 | const ScrollableContent = styled.div` 188 | padding: 0 20px; 189 | padding-bottom: 150px; 190 | `; 191 | 192 | const Overlay = styled.div` 193 | position: absolute; 194 | left: 0; 195 | right: 0; 196 | bottom: 0; 197 | top: 0; 198 | background: rgba(0, 0, 0, 0.8); 199 | `; 200 | 201 | export default CodePage; 202 | -------------------------------------------------------------------------------- /src/components/Credits.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | interface IProps {} 5 | 6 | const Credits: React.FC = ({}) => { 7 | return ( 8 | 13 | epranka.com 14 | 15 | ); 16 | }; 17 | 18 | const CreditsAnchor = styled.a` 19 | position: fixed; 20 | top: 12px; 21 | left: 20px; 22 | color: white; 23 | z-index: 3; 24 | font-weight: bold; 25 | 26 | img { 27 | width: 24px; 28 | margin-right: 4px; 29 | } 30 | 31 | &:hover { 32 | color: white; 33 | text-decoration: none; 34 | } 35 | `; 36 | 37 | export default Credits; 38 | -------------------------------------------------------------------------------- /src/components/OrchestraContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from "react"; 2 | import Conductor from "../classes/Conductor"; 3 | import Orchestra from "../classes/Orchestra"; 4 | 5 | type OrchestraContextType = [ 6 | { 7 | orchestra: React.MutableRefObject; 8 | conductor: React.MutableRefObject; 9 | estimatedTime: number; 10 | }, 11 | (source: string) => any 12 | ]; 13 | 14 | const OrchestraContext = React.createContext([ 15 | { 16 | orchestra: { current: undefined }, 17 | conductor: { current: undefined }, 18 | estimatedTime: 0 19 | }, 20 | () => {} 21 | ]); 22 | 23 | interface IProviderProps {} 24 | 25 | export const OrchestraContextProvider: React.SFC = ({ 26 | children 27 | }) => { 28 | const [estimatedTime, setEstimatedTime] = useState(0); 29 | 30 | const orchestraRef = useRef(); 31 | const conductorRef = useRef(); 32 | 33 | const onSourceChange = (source: string) => { 34 | if (conductorRef.current) { 35 | conductorRef.current.destroy(); 36 | } 37 | const orchestra = new Orchestra(source); 38 | orchestraRef.current = orchestra; 39 | const conductor = new Conductor(orchestra); 40 | conductorRef.current = conductor; 41 | setEstimatedTime(conductor.getDurationInSeconds()); 42 | }; 43 | 44 | return ( 45 | 53 | {children} 54 | 55 | ); 56 | }; 57 | 58 | export default OrchestraContext; 59 | -------------------------------------------------------------------------------- /src/components/PlayPage.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | useContext, 3 | useEffect, 4 | useState, 5 | useRef, 6 | useCallback 7 | } from "react"; 8 | import styled from "styled-components"; 9 | import OrchestraContext from "./OrchestraContext"; 10 | import posed from "react-pose"; 11 | import AudioRecorder from "../classes/AudioRecorder"; 12 | import Visualizer from "./Visualizer"; 13 | 14 | interface IProps { 15 | show: boolean; 16 | onStopClick: () => any; 17 | } 18 | 19 | type PlayStateType = "playing" | "paused" | "ended"; 20 | 21 | const PlayPage: React.SFC = ({ show, onStopClick }) => { 22 | const [{ conductor, estimatedTime }] = useContext(OrchestraContext); 23 | const timerRef = useRef(); 24 | const timeLeftRef = useRef(0); 25 | const [timeLeft, setTimeLeft] = useState(0); 26 | const [playState, setPlayState] = useState("playing"); 27 | 28 | const startTimer = () => { 29 | timeLeftRef.current = estimatedTime; 30 | timerRef.current = setInterval(() => { 31 | timeLeftRef.current = Math.max(timeLeftRef.current - 1, 0); 32 | setTimeLeft(timeLeftRef.current); 33 | }, 1000); 34 | }; 35 | 36 | const stopTimer = () => { 37 | if (timerRef.current) clearInterval(timerRef.current); 38 | timeLeftRef.current = estimatedTime; 39 | setTimeLeft(timeLeftRef.current); 40 | }; 41 | 42 | const handleOrchestraEnd = useCallback(() => { 43 | if (timerRef.current) { 44 | clearInterval(timerRef.current); 45 | } 46 | setPlayState("ended"); 47 | }, []); 48 | 49 | const handleControlButton = () => { 50 | if (playState === "playing") { 51 | onStopClick(); 52 | } else if (playState === "ended") { 53 | play(); 54 | } 55 | }; 56 | 57 | const invokeAudioRecordDownload = () => { 58 | const audioBlob = AudioRecorder.getAudioRecordBlob(); 59 | const url = URL.createObjectURL(audioBlob); 60 | const a = document.createElement("a"); 61 | a.href = url; 62 | a.download = "The-Little-Orchestra.ogg"; 63 | 64 | const clickHandler = () => { 65 | setTimeout(() => { 66 | URL.revokeObjectURL(url); 67 | }, 10); 68 | }; 69 | 70 | a.addEventListener("click", clickHandler, false); 71 | a.click(); 72 | }; 73 | 74 | const play = () => { 75 | setPlayState("playing"); 76 | setTimeLeft(estimatedTime); 77 | setTimeout(() => { 78 | startTimer(); 79 | if (conductor.current) { 80 | conductor.current.play(); 81 | conductor.current.on("end", handleOrchestraEnd); 82 | } 83 | }, 1500); 84 | }; 85 | 86 | const stop = () => { 87 | setTimeout(() => { 88 | if (conductor.current) { 89 | conductor.current.off("end", handleOrchestraEnd); 90 | conductor.current.stop(); 91 | stopTimer(); 92 | } 93 | }, 1000); 94 | }; 95 | 96 | useEffect(() => { 97 | timeLeftRef.current = estimatedTime; 98 | setTimeLeft(timeLeftRef.current); 99 | }, [estimatedTime]); 100 | 101 | useEffect(() => { 102 | if (show) { 103 | play(); 104 | } else { 105 | stop(); 106 | } 107 | }, [show]); 108 | 109 | return ( 110 | 111 | 112 | 113 |
114 | {timeLeft} s 115 | 116 |
117 | {playState === "ended" ? ( 118 | Exit 119 | ) : null} 120 | 121 | {playState === "playing" 122 | ? "Stop" 123 | : playState === "paused" 124 | ? "Play" 125 | : "Repeat"} 126 | 127 |
128 | {playState === "ended" && AudioRecorder.isAvailable() ? ( 129 | 130 | ↓ Download 131 | 132 | ) : null} 133 |
134 |
135 |
136 | ); 137 | }; 138 | 139 | const ControlButtonContainer = styled.div``; 140 | 141 | const ControlButton = styled.div` 142 | display: block; 143 | text-align: center; 144 | font-size: 1.25rem; 145 | border: 2px solid #9e9e9e; 146 | border-radius: 0.125rem; 147 | padding: 10px 30px; 148 | cursor: pointer; 149 | 150 | margin: 0 5px; 151 | margin-bottom: 0.5rem; 152 | 153 | transition: background 0.25s ease; 154 | user-select: none; 155 | 156 | &:hover { 157 | background: rgba(32, 33, 36, 0.8); 158 | } 159 | `; 160 | 161 | const Timer = styled.div` 162 | font-size: 8vmax; 163 | display: block; 164 | user-select: none; 165 | `; 166 | 167 | const Center = styled.div` 168 | position: absolute; 169 | top: 50%; 170 | left: 50%; 171 | transform: translate(-50%, -50%); 172 | color: #eee; 173 | text-align: center; 174 | z-index: 3; 175 | `; 176 | 177 | const PlayPagePosed = posed.div({ 178 | show: { 179 | opacity: 1, 180 | applyAtStart: { 181 | display: "block" 182 | } 183 | }, 184 | hide: { 185 | opacity: 0, 186 | applyAtEnd: { 187 | display: "none" 188 | } 189 | } 190 | }); 191 | 192 | const PlayPageStyled = styled(PlayPagePosed)` 193 | background: black; 194 | position: fixed; 195 | top: 0; 196 | left: 0; 197 | right: 0; 198 | bottom: 0; 199 | background-image: url("/play-bg.jpg"); 200 | background-size: cover; 201 | background-position: center center; 202 | display: flex; 203 | align-items: center; 204 | `; 205 | 206 | const Overlay = styled.div` 207 | position: absolute; 208 | left: 0; 209 | right: 0; 210 | bottom: 0; 211 | top: 0; 212 | background: rgba(0, 0, 0, 0.7); 213 | z-index: 1; 214 | `; 215 | 216 | export default PlayPage; 217 | -------------------------------------------------------------------------------- /src/components/Preloader.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import styled from "styled-components"; 3 | import { Container, Row, Col, Spinner } from "react-bootstrap"; 4 | import drums from "../instruments/drums"; 5 | import piano from "../instruments/piano"; 6 | import cello from "../instruments/cello"; 7 | import harp from "../instruments/harp"; 8 | import flute from "../instruments/flute"; 9 | import tubaSTC from "../instruments/tuba-stc"; 10 | import bass from "../instruments/bass"; 11 | import chorusFemale from "../instruments/chorusFemale"; 12 | import posed from "react-pose"; 13 | 14 | interface IProps { 15 | onLoad: () => any; 16 | show: boolean; 17 | } 18 | 19 | const instruments = [ 20 | drums, 21 | piano, 22 | cello, 23 | harp, 24 | flute, 25 | tubaSTC, 26 | bass, 27 | chorusFemale 28 | ]; 29 | 30 | const images = ["/preloader-bg.jpg", "/code-bg.jpg", "/play-bg.jpg"]; 31 | 32 | const preloadImage = (src: string) => { 33 | return new Promise(resolve => { 34 | const $image = document.createElement("img"); 35 | $image.src = src; 36 | if ($image.complete) { 37 | resolve(); 38 | } else { 39 | $image.onload = $image.onerror = resolve; 40 | } 41 | }); 42 | }; 43 | 44 | const Preloader: React.SFC = ({ show, onLoad }) => { 45 | const [currentInstrument, setCurrentInstrument] = useState(""); 46 | 47 | const loadInstruments = async () => { 48 | for (const image of images) { 49 | await preloadImage(image); 50 | } 51 | for (const instrument of instruments) { 52 | setCurrentInstrument(instrument.label); 53 | await instrument.load(); 54 | } 55 | setCurrentInstrument(""); 56 | onLoad(); 57 | }; 58 | 59 | useEffect(() => { 60 | loadInstruments(); 61 | }, []); 62 | 63 | return ( 64 | 65 | 66 | 67 | 68 | 69 | 𝄞 70 | 71 | Loading {currentInstrument} sound fonts... 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | ); 81 | }; 82 | 83 | const LoadingLabel = styled.h3` 84 | color: #eee; 85 | `; 86 | 87 | const TrebleClef = styled.div` 88 | color: #eee; 89 | `; 90 | 91 | const LoadingSpinner = styled.div``; 92 | 93 | const Overlay = styled.div` 94 | position: absolute; 95 | left: 0; 96 | right: 0; 97 | bottom: 0; 98 | top: 0; 99 | background: rgba(0, 0, 0, 0.6); 100 | `; 101 | 102 | const PreloaderPosed = posed.div({ 103 | show: { 104 | opacity: 1.0 105 | }, 106 | hide: { 107 | opacity: 0.0, 108 | applyAtEnd: { 109 | display: "none" 110 | } 111 | } 112 | }); 113 | 114 | const PreloaderStyled = styled(PreloaderPosed)` 115 | background: black; 116 | position: fixed; 117 | top: 0; 118 | left: 0; 119 | right: 0; 120 | bottom: 0; 121 | background-image: url("/preloader-bg.jpg"); 122 | background-size: cover; 123 | background-position: center center; 124 | display: flex; 125 | align-items: center; 126 | `; 127 | 128 | export default Preloader; 129 | -------------------------------------------------------------------------------- /src/components/Visualizer.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useContext, useCallback } from "react"; 2 | import { useWindowSize } from "react-use"; 3 | import styled from "styled-components"; 4 | import OrchestraContext from "./OrchestraContext"; 5 | import { gsap } from "gsap"; 6 | 7 | interface IProps {} 8 | 9 | class Item { 10 | $dom: HTMLElement; 11 | visible: boolean = false; 12 | constructor() { 13 | this.$dom = document.createElement("div"); 14 | this.$dom.style.position = "absolute"; 15 | this.hide(); 16 | this.setText(""); 17 | } 18 | 19 | public hide() { 20 | this.$dom.style.opacity = "0"; 21 | this.visible = false; 22 | } 23 | 24 | public show() { 25 | this.$dom.style.opacity = "1"; 26 | this.visible = true; 27 | } 28 | 29 | public fadeIn() { 30 | return new Promise(resolve => { 31 | if (this.visible) return resolve(); 32 | gsap.to(this.$dom, 0.25, { 33 | opacity: 1, 34 | onComplete: () => { 35 | this.visible = true; 36 | resolve(); 37 | } 38 | }); 39 | }); 40 | } 41 | 42 | public fadeOut() { 43 | return new Promise(resolve => { 44 | if (!this.visible) return resolve(); 45 | gsap.to(this.$dom, 0.25, { 46 | opacity: 0, 47 | onComplete: () => { 48 | this.visible = false; 49 | resolve(); 50 | } 51 | }); 52 | }); 53 | } 54 | 55 | public slowFadeOut() { 56 | return new Promise(resolve => { 57 | if (!this.visible) resolve(); 58 | gsap.to(this.$dom, 7, { 59 | opacity: 0, 60 | onComplete: () => { 61 | this.visible = false; 62 | resolve(); 63 | } 64 | }); 65 | }); 66 | } 67 | 68 | public setText(text: string) { 69 | if (text.length > 24) { 70 | text = text.substring(0, 24) + "..."; 71 | } 72 | this.$dom.innerText = text; 73 | } 74 | 75 | public setPosition(x: number, y: number) { 76 | gsap.set(this.$dom, { x, y }); 77 | } 78 | } 79 | 80 | const Visualizer: React.SFC = ({}) => { 81 | const $visualizerRef = useRef(null); 82 | const pointer = useRef(0); 83 | const items = useRef([]); 84 | 85 | const [{ conductor }] = useContext(OrchestraContext); 86 | 87 | const { width: viewportW, height: viewportH } = useWindowSize(); 88 | 89 | const createItem = () => { 90 | const item = new Item(); 91 | $visualizerRef.current?.appendChild(item.$dom); 92 | items.current.push(item); 93 | }; 94 | 95 | const updateItems = async (text: string) => { 96 | pointer.current = (pointer.current + 1) % items.current.length; 97 | const item = items.current[pointer.current]; 98 | await item.fadeOut(); 99 | const [x, y] = [Math.random() * viewportW, Math.random() * viewportH]; 100 | item.setPosition(x, y); 101 | item.setText(text); 102 | await item.fadeIn(); 103 | item.slowFadeOut(); 104 | }; 105 | 106 | const handleNotify = useCallback((text: string) => { 107 | updateItems(text); 108 | }, []); 109 | 110 | useEffect(() => { 111 | if (conductor.current) { 112 | conductor.current.on("notify", handleNotify); 113 | } 114 | return () => { 115 | if (conductor.current) { 116 | conductor.current.off("notify", handleNotify); 117 | } 118 | }; 119 | }, [conductor.current]); 120 | 121 | useEffect(() => { 122 | for (let i = 0; i < 40; i++) { 123 | createItem(); 124 | } 125 | }, []); 126 | return ; 127 | }; 128 | 129 | const VisualizerStyled = styled.div` 130 | position: absolute; 131 | top: 0; 132 | left: 0; 133 | right: 0; 134 | bottom: 0; 135 | z-index: 2; 136 | color: #dadada; 137 | font-size: 2vmax; 138 | user-select: none; 139 | `; 140 | 141 | export default Visualizer; 142 | -------------------------------------------------------------------------------- /src/defaultSource.ts: -------------------------------------------------------------------------------- 1 | const defaultSource = `import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {Greeter as Greetifier, GreeterProps as GreeterProps} from 'greeter'; 4 | 5 | function getRandomGreeting() { 6 | switch (Math.floor(Math.random() * 4)) { 7 | case 0: return 'Hello'; 8 | case 1: return 'Howdy'; 9 | case 2: return 'Greetings to you'; 10 | case 3: return 'Hail'; 11 | } 12 | } 13 | 14 | (() => { 15 | let props: GreeterProps = { 16 | whomToGreet: 'world!', 17 | }; 18 | 19 | ReactDOM.render( 20 | , 21 | $('#output').get(0) 22 | ); 23 | })();`; 24 | 25 | export default defaultSource; 26 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: 'Alegreya Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "bootstrap/dist/css/bootstrap.min.css"; 4 | import "./index.css"; 5 | import * as serviceWorker from "./serviceWorker"; 6 | import App from "./components/App"; 7 | 8 | ReactDOM.render(, document.getElementById("root")); 9 | 10 | // If you want your app to work offline and load faster, you can change 11 | // unregister() to register() below. Note this comes with some pitfalls. 12 | // Learn more about service workers: https://bit.ly/CRA-PWA 13 | serviceWorker.unregister(); 14 | -------------------------------------------------------------------------------- /src/instruments/Instrument.ts: -------------------------------------------------------------------------------- 1 | import AudioRecorder from "../classes/AudioRecorder"; 2 | 3 | interface ICallbacks { 4 | load: (() => any)[]; 5 | } 6 | 7 | class Instrument { 8 | name: string; 9 | label: string; 10 | loaded: boolean = false; 11 | private callbacks: ICallbacks = { 12 | load: [] 13 | }; 14 | private instance: any; 15 | private ToneType: any; 16 | private samples: any; 17 | constructor(name, ToneType, samples) { 18 | this.ToneType = ToneType; 19 | this.samples = samples; 20 | this.name = name; 21 | 22 | this.handleOnLoad = this.handleOnLoad.bind(this); 23 | } 24 | 25 | public async load() { 26 | const self = this; 27 | return new Promise(resolve => { 28 | self.instance = new self.ToneType(self.samples, () => { 29 | self.handleOnLoad(); 30 | self.instance.volume.value -= 8; 31 | if (AudioRecorder.isAvailable()) { 32 | self.instance.connect(AudioRecorder.destination); 33 | } 34 | resolve(); 35 | }).toMaster(); 36 | }); 37 | } 38 | 39 | public on(type: T, callback: ICallbacks[T][0]) { 40 | if (!this.callbacks[type].includes(callback as any)) { 41 | this.callbacks[type].push(callback as any); 42 | } 43 | 44 | // handle already loaded 45 | if (type === "load" && this.loaded) { 46 | callback(); 47 | } 48 | } 49 | 50 | public off(type: T, callback: ICallbacks[T][0]) { 51 | this.callbacks[type] = this.callbacks[type].filter( 52 | c => c !== callback 53 | ) as any; 54 | } 55 | 56 | public it() { 57 | if (!this.instance) 58 | throw new Error("Load instrument before use it: " + this.name); 59 | return this.instance; 60 | } 61 | 62 | private handleOnLoad() { 63 | this.loaded = true; 64 | for (const callback of this.callbacks["load"]) { 65 | callback(); 66 | } 67 | } 68 | } 69 | 70 | export default Instrument; 71 | -------------------------------------------------------------------------------- /src/instruments/bass.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const bass = new Instrument("bass", Tone.Sampler, { 5 | C3: "./ogg/bass/boffner1.ogg", 6 | C4: "./ogg/bass/boffner2.ogg" 7 | }); 8 | 9 | bass.label = "Bass"; 10 | 11 | export default bass; 12 | -------------------------------------------------------------------------------- /src/instruments/cello.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const cello = new Instrument("cello", Tone.Sampler, { 5 | A2: "./ogg/cello/cello-a2.ogg", 6 | A3: "./ogg/cello/cello-a3.ogg", 7 | A4: "./ogg/cello/cello-a4.ogg", 8 | A5: "./ogg/cello/cello-a5.ogg", 9 | C2: "./ogg/cello/cello-c2.ogg", 10 | C3: "./ogg/cello/cello-c3.ogg", 11 | C4: "./ogg/cello/cello-c4.ogg", 12 | C5: "./ogg/cello/cello-c5.ogg", 13 | "D#2": "./ogg/cello/cello-ds2.ogg", 14 | "D#3": "./ogg/cello/cello-ds3.ogg", 15 | "D#4": "./ogg/cello/cello-ds4.ogg", 16 | "D#5": "./ogg/cello/cello-ds5.ogg", 17 | "F#2": "./ogg/cello/cello-fs2.ogg", 18 | "F#3": "./ogg/cello/cello-fs3.ogg", 19 | "F#4": "./ogg/cello/cello-fs4.ogg", 20 | "F#5": "./ogg/cello/cello-fs5.ogg" 21 | }); 22 | 23 | cello.label = "Cello"; 24 | 25 | export default cello; 26 | -------------------------------------------------------------------------------- /src/instruments/chorusFemale.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const chorusFemale = new Instrument("chorus-female", Tone.Sampler, { 5 | A4: "./ogg/chorus/chorus-female-a4.ogg", 6 | A5: "./ogg/chorus/chorus-female-a5.ogg", 7 | "A#4": "./ogg/chorus/chorus-female-as4.ogg", 8 | "A#5": "./ogg/chorus/chorus-female-as5.ogg", 9 | B4: "./ogg/chorus/chorus-female-b4.ogg", 10 | B5: "./ogg/chorus/chorus-female-b5.ogg", 11 | C5: "./ogg/chorus/chorus-female-c5.ogg", 12 | C6: "./ogg/chorus/chorus-female-c6.ogg", 13 | D5: "./ogg/chorus/chorus-female-d5.ogg", 14 | "D#5": "./ogg/chorus/chorus-female-ds5.ogg", 15 | E5: "./ogg/chorus/chorus-female-e5.ogg", 16 | F5: "./ogg/chorus/chorus-female-f5.ogg", 17 | "F#5": "./ogg/chorus/chorus-female-fs5.ogg", 18 | G4: "./ogg/chorus/chorus-female-g4.ogg", 19 | G5: "./ogg/chorus/chorus-female-g5.ogg", 20 | "G#4": "./ogg/chorus/chorus-female-gs4.ogg", 21 | "G#5": "./ogg/chorus/chorus-female-gs5.ogg" 22 | }); 23 | 24 | chorusFemale.label = "Chorus"; 25 | 26 | export default chorusFemale; 27 | -------------------------------------------------------------------------------- /src/instruments/drums.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const drums = new Instrument("drums", Tone.Players, { 5 | kick: "./ogg/drums/Kick.ogg", 6 | openhat: "./ogg/drums/OpenHat.ogg", 7 | closedhat: "./ogg/drums/ClosedHat.ogg", 8 | clap: "./ogg/drums/Clap.ogg" 9 | }); 10 | 11 | drums.label = "Drums"; 12 | 13 | export default drums; 14 | -------------------------------------------------------------------------------- /src/instruments/flute.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const flute = new Instrument("flute", Tone.Sampler, { 5 | A3: "./ogg/flute/flute-a3.ogg", 6 | A4: "./ogg/flute/flute-a4.ogg", 7 | A5: "./ogg/flute/flute-a5.ogg", 8 | C3: "./ogg/flute/flute-c3.ogg", 9 | C4: "./ogg/flute/flute-c4.ogg", 10 | C5: "./ogg/flute/flute-c5.ogg", 11 | C6: "./ogg/flute/flute-c6.ogg", 12 | "D#3": "./ogg/flute/flute-ds3.ogg", 13 | "D#4": "./ogg/flute/flute-ds4.ogg", 14 | "D#5": "./ogg/flute/flute-ds5.ogg", 15 | "F#3": "./ogg/flute/flute-fs3.ogg", 16 | "F#4": "./ogg/flute/flute-fs4.ogg", 17 | "F#5": "./ogg/flute/flute-fs5.ogg" 18 | }); 19 | 20 | flute.label = "Flute"; 21 | 22 | export default flute; 23 | -------------------------------------------------------------------------------- /src/instruments/harp.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const harp = new Instrument("harp", Tone.Sampler, { 5 | A2: "./ogg/harp/harp-a2.ogg", 6 | A3: "./ogg/harp/harp-a3.ogg", 7 | A4: "./ogg/harp/harp-a4.ogg", 8 | A5: "./ogg/harp/harp-a5.ogg", 9 | A6: "./ogg/harp/harp-a6.ogg", 10 | C2: "./ogg/harp/harp-c2.ogg", 11 | C3: "./ogg/harp/harp-c3.ogg", 12 | C4: "./ogg/harp/harp-c4.ogg", 13 | C5: "./ogg/harp/harp-c5.ogg", 14 | C6: "./ogg/harp/harp-c6.ogg", 15 | C7: "./ogg/harp/harp-c7.ogg", 16 | "D#2": "./ogg/harp/harp-ds2.ogg", 17 | "D#3": "./ogg/harp/harp-ds3.ogg", 18 | "D#4": "./ogg/harp/harp-ds4.ogg", 19 | "D#5": "./ogg/harp/harp-ds5.ogg", 20 | "D#6": "./ogg/harp/harp-ds6.ogg", 21 | "F#2": "./ogg/harp/harp-fs2.ogg", 22 | "F#3": "./ogg/harp/harp-fs3.ogg", 23 | "F#4": "./ogg/harp/harp-fs4.ogg", 24 | "F#5": "./ogg/harp/harp-fs5.ogg", 25 | "F#6": "./ogg/harp/harp-fs6.ogg" 26 | }); 27 | 28 | harp.label = "Harp"; 29 | 30 | export default harp; 31 | -------------------------------------------------------------------------------- /src/instruments/piano.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const getSamplerData = () => { 5 | const velocity = 10; 6 | const notes = ["A", "C", "D#", "F#"]; 7 | const octaves = [1, 2, 3, 4, 5, 6, 7]; 8 | return octaves.reduce((acc, octave) => { 9 | return notes.reduce((acc, note) => { 10 | acc[note + octave] = 11 | "./ogg/piano/" + 12 | note.replace("#", "s") + 13 | octave + 14 | "v" + 15 | velocity + 16 | ".ogg"; 17 | return acc; 18 | }, acc); 19 | }, {}); 20 | }; 21 | // const piano = new Tone.Sampler(getSamplerData()).toMaster(); 22 | 23 | // piano.name = "piano"; 24 | 25 | const piano = new Instrument("piano", Tone.Sampler, getSamplerData()); 26 | 27 | piano.label = "Piano"; 28 | 29 | export default piano; 30 | -------------------------------------------------------------------------------- /src/instruments/tuba-stc.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import Instrument from "./Instrument"; 3 | 4 | const tubaSTC = new Instrument("tuba-stc", Tone.Sampler, { 5 | "A#1": "./ogg/tuba-stc/tuba-stc-rr1-as1.ogg", 6 | "A#2": "./ogg/tuba-stc/tuba-stc-rr1-as2.ogg", 7 | "A#3": "./ogg/tuba-stc/tuba-stc-rr1-as3.ogg", 8 | "C#2": "./ogg/tuba-stc/tuba-stc-rr1-cs2.ogg", 9 | "C#3": "./ogg/tuba-stc/tuba-stc-rr1-cs3.ogg", 10 | "C#4": "./ogg/tuba-stc/tuba-stc-rr1-cs4.ogg", 11 | E1: "./ogg/tuba-stc/tuba-stc-rr1-e1.ogg", 12 | E2: "./ogg/tuba-stc/tuba-stc-rr1-e2.ogg", 13 | E3: "./ogg/tuba-stc/tuba-stc-rr1-e3.ogg", 14 | G1: "./ogg/tuba-stc/tuba-stc-rr1-g1.ogg", 15 | G2: "./ogg/tuba-stc/tuba-stc-rr1-g2.ogg", 16 | G3: "./ogg/tuba-stc/tuba-stc-rr1-g3.ogg" 17 | }); 18 | 19 | tubaSTC.label = "Tuba"; 20 | 21 | export default tubaSTC; 22 | -------------------------------------------------------------------------------- /src/isDebug.ts: -------------------------------------------------------------------------------- 1 | const isDebug = process.env.NODE_ENV === "development"; 2 | 3 | export default isDebug; 4 | -------------------------------------------------------------------------------- /src/players/createNotesPlayer.ts: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import { INote } from "../types"; 3 | import { note as parseNote } from "@tonaljs/tonal"; 4 | import ts from "typescript"; 5 | import Instrument from "../instruments/Instrument"; 6 | 7 | interface INotesPlayerOptions { 8 | instrument: Instrument; 9 | inotes: INote[]; 10 | repeat: boolean; 11 | } 12 | 13 | interface ICallbacks { 14 | end: (() => any)[]; 15 | note: (( 16 | instrumentName: string, 17 | note: string | string[], 18 | node: ts.Node | null 19 | ) => any)[]; 20 | } 21 | 22 | export class INotesPlayer { 23 | instrument: Instrument; 24 | inotes: INote[]; 25 | repeat: boolean; 26 | private resolution: number = 128; 27 | private loop: any; 28 | private note_counter: number = 0; 29 | private bar_counter: number = 0; 30 | private bar_fill: number = 0; 31 | private bar: number = 0; 32 | private callbacks: ICallbacks = { 33 | end: [], 34 | note: [] 35 | }; 36 | constructor(options: INotesPlayerOptions) { 37 | this.instrument = options.instrument; 38 | this.inotes = options.inotes; 39 | this.repeat = options.repeat; 40 | 41 | this.loop = new Tone.Loop( 42 | this.loopCallback.bind(this), 43 | this.resolution + "n" 44 | ); 45 | 46 | this.handleTransportStart = this.handleTransportStart.bind(this); 47 | this.handleTransportStop = this.handleTransportStop.bind(this); 48 | 49 | Tone.Transport.on("start", this.handleTransportStart); 50 | Tone.Transport.on("stop", this.handleTransportStop); 51 | } 52 | 53 | public start() { 54 | this.loop.start(); 55 | } 56 | 57 | public stop() { 58 | this.loop.stop(); 59 | } 60 | 61 | private notifyEnd() { 62 | if (this.callbacks["end"]) { 63 | for (const callback of this.callbacks["end"]) { 64 | callback(); 65 | } 66 | } 67 | } 68 | 69 | public destroy() { 70 | Tone.Transport.off("stop", this.handleTransportStop); 71 | this.callbacks = { end: [], note: [] }; 72 | } 73 | 74 | public on(type: T, callback: ICallbacks[T][0]) { 75 | if (!this.callbacks[type].includes(callback as any)) { 76 | this.callbacks[type].push(callback as any); 77 | } 78 | } 79 | 80 | public off(type: T, callback: ICallbacks[T][0]) { 81 | this.callbacks[type] = this.callbacks[type].filter( 82 | c => c !== callback 83 | ) as any; 84 | } 85 | 86 | private handleTransportStop() { 87 | this.note_counter = 0; 88 | this.bar_counter = 0; 89 | this.bar_fill = 0; 90 | this.bar = 0; 91 | } 92 | 93 | private handleTransportStart() { 94 | if (this.inotes.length == 0) { 95 | this.notifyEnd(); 96 | } 97 | } 98 | 99 | private notify(note: string | string[], node: ts.Node | null) { 100 | if (this.callbacks["note"]) { 101 | for (const callback of this.callbacks["note"]) { 102 | callback(this.instrument.name, note, node); 103 | } 104 | } 105 | } 106 | 107 | private isNoteValid(note: string | string[]) { 108 | const notes = Array.isArray(note) ? note : [note]; 109 | let isValid = true; 110 | for (const note of notes) { 111 | const parsed = parseNote(note); 112 | if (parsed.empty) { 113 | isValid = false; 114 | } 115 | } 116 | return isValid; 117 | } 118 | 119 | private loopCallback(time) { 120 | // manage bar fill 121 | if (this.bar_fill > 0) this.bar_fill--; 122 | // manage notes 123 | if (this.bar_fill === 0 && this.note_counter < this.inotes.length) { 124 | const { note, duration, velocity, sustain, play, display } = this.inotes[ 125 | this.note_counter 126 | ]; 127 | this.bar_fill += this.resolution / duration; 128 | const durationWithSustain = sustain ? sustain : duration; 129 | if (note !== "r") { 130 | this.notify(note, display); 131 | if (typeof play === "function") { 132 | if (Array.isArray(note)) { 133 | for (const n of note) { 134 | play(this.instrument, n, duration, time, velocity); 135 | } 136 | } else { 137 | play(this.instrument, note, duration, time, velocity); 138 | } 139 | } else { 140 | if (this.isNoteValid(note)) { 141 | this.instrument 142 | .it() 143 | .triggerAttackRelease( 144 | note, 145 | durationWithSustain + "n", 146 | time, 147 | velocity 148 | ); 149 | } 150 | } 151 | } 152 | this.note_counter++; 153 | if (this.note_counter >= this.inotes.length) { 154 | if (this.repeat) { 155 | this.note_counter = 0; 156 | } else { 157 | this.notifyEnd(); 158 | } 159 | } 160 | } 161 | 162 | // manage bar 163 | this.bar_counter++; 164 | if (this.bar_counter === this.resolution) { 165 | this.bar_counter = 0; 166 | this.bar++; 167 | } 168 | } 169 | } 170 | 171 | const createNotesPlayer = ( 172 | instrument, 173 | notes: INote[] = [], 174 | repeat: boolean = false 175 | ) => { 176 | return new INotesPlayer({ instrument, inotes: notes, repeat }); 177 | }; 178 | 179 | export default createNotesPlayer; 180 | -------------------------------------------------------------------------------- /src/players/createTimeNotesPlayer.ts: -------------------------------------------------------------------------------- 1 | import { ITimeNote } from "../types"; 2 | import * as Tone from "tone"; 3 | import ts from "typescript"; 4 | import Instrument from "../instruments/Instrument"; 5 | import { note as parseNote } from "@tonaljs/tonal"; 6 | 7 | interface ITimeNotesPlayerOptions { 8 | instrument: Instrument; 9 | itnotes: ITimeNote[]; 10 | } 11 | 12 | interface ICallbacks { 13 | note: (( 14 | instrumentName: string, 15 | note: string | string[], 16 | node: ts.Node | null 17 | ) => any)[]; 18 | } 19 | 20 | export class ITimeNotesPlayer { 21 | instrument: Instrument; 22 | itnotes: ITimeNote[]; 23 | 24 | private resolution: number = 128; 25 | private bar_counter = 0; 26 | private bar = 0; 27 | private notesByBarAndBeat: { 28 | [bar: number]: { [beat: number]: ITimeNote[] }; 29 | } = {}; 30 | private notesByBeat: { 31 | [beat: number]: ITimeNote[]; 32 | } = {}; 33 | private loop: any; 34 | 35 | private callbacks: ICallbacks = { 36 | note: [] 37 | }; 38 | 39 | constructor(options: ITimeNotesPlayerOptions) { 40 | this.instrument = options.instrument; 41 | this.itnotes = options.itnotes; 42 | this.sortITNotes(); 43 | 44 | this.loop = new Tone.Loop( 45 | this.loopCallback.bind(this), 46 | this.resolution + "n" 47 | ); 48 | 49 | this.handleTransportStop = this.handleTransportStop.bind(this); 50 | 51 | Tone.Transport.on("stop", this.handleTransportStop); 52 | } 53 | 54 | public start() { 55 | this.loop.start(); 56 | } 57 | 58 | public stop() { 59 | this.loop.stop(); 60 | } 61 | 62 | public destroy() { 63 | Tone.Transport.off("stop", this.handleTransportStop); 64 | this.callbacks = { note: [] }; 65 | } 66 | 67 | private handleTransportStop() { 68 | this.bar_counter = 0; 69 | this.bar = 0; 70 | } 71 | 72 | public on(type: T, callback: ICallbacks[T][0]) { 73 | if (!this.callbacks[type].includes(callback as any)) { 74 | this.callbacks[type].push(callback as any); 75 | } 76 | } 77 | 78 | public off(type: T, callback: ICallbacks[T][0]) { 79 | this.callbacks[type] = this.callbacks[type].filter( 80 | c => c !== callback 81 | ) as any; 82 | } 83 | 84 | private notify(note: string | string[], node: ts.Node | null) { 85 | if (this.callbacks["note"]) { 86 | for (const callback of this.callbacks["note"]) { 87 | callback(this.instrument.name, note, node); 88 | } 89 | } 90 | } 91 | 92 | private isNoteValid(note: string | string[]) { 93 | const notes = Array.isArray(note) ? note : [note]; 94 | let isValid = true; 95 | for (const note of notes) { 96 | const parsed = parseNote(note); 97 | if (parsed.empty) { 98 | isValid = false; 99 | } 100 | } 101 | return isValid; 102 | } 103 | 104 | private loopCallback(time) { 105 | const beat = this.bar_counter / this.resolution; 106 | 107 | const itnotesToPlayByBarAndBeats = 108 | (this.notesByBarAndBeat[this.bar] && 109 | this.notesByBarAndBeat[this.bar][beat]) || 110 | []; 111 | const itnotesToPlayByBeats = this.notesByBeat[beat] || []; 112 | const itnotesToPlay = [ 113 | ...itnotesToPlayByBarAndBeats, 114 | ...itnotesToPlayByBeats 115 | ]; 116 | for (const itnote of itnotesToPlay) { 117 | if (itnote.note !== "r") { 118 | this.notify(itnote.note, itnote.display); 119 | } 120 | if (typeof itnote.play === "function") { 121 | itnote.play( 122 | this.instrument, 123 | itnote.note, 124 | itnote.duration, 125 | time, 126 | itnote.velocity 127 | ); 128 | } else { 129 | if (this.isNoteValid(itnote.note)) { 130 | this.instrument 131 | .it() 132 | .triggerAttackRelease( 133 | itnote.note, 134 | itnote.duration + "n", 135 | time, 136 | itnote.velocity 137 | ); 138 | } 139 | } 140 | } 141 | this.bar_counter++; 142 | if (this.bar_counter === this.resolution) { 143 | this.bar_counter = 0; 144 | this.bar++; 145 | } 146 | } 147 | 148 | private sortITNotes() { 149 | this.notesByBarAndBeat = this.itnotes.reduce((acc, itnote) => { 150 | if (typeof itnote.bar === "number") { 151 | if (!acc[itnote.bar]) acc[itnote.bar] = {}; 152 | if (!acc[itnote.bar][itnote.beat]) acc[itnote.bar][itnote.beat] = []; 153 | acc[itnote.bar][itnote.beat].push(itnote); 154 | return acc; 155 | } else return acc; 156 | }, {}); 157 | 158 | this.notesByBeat = this.itnotes.reduce((acc, itnote) => { 159 | if (typeof itnote.bar === "undefined") { 160 | if (!acc[itnote.beat]) acc[itnote.beat] = []; 161 | acc[itnote.beat].push(itnote); 162 | return acc; 163 | } else return acc; 164 | }, {}); 165 | } 166 | } 167 | 168 | const createTimeNotesPlayer = (instrument, itnotes: ITimeNote[] = []) => { 169 | return new ITimeNotesPlayer({ instrument, itnotes }); 170 | }; 171 | 172 | export default createTimeNotesPlayer; 173 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | declare namespace NodeJS { 6 | interface ProcessEnv { 7 | readonly NODE_ENV: 'development' | 'production' | 'test'; 8 | readonly PUBLIC_URL: string; 9 | } 10 | } 11 | 12 | declare module '*.bmp' { 13 | const src: string; 14 | export default src; 15 | } 16 | 17 | declare module '*.gif' { 18 | const src: string; 19 | export default src; 20 | } 21 | 22 | declare module '*.jpg' { 23 | const src: string; 24 | export default src; 25 | } 26 | 27 | declare module '*.jpeg' { 28 | const src: string; 29 | export default src; 30 | } 31 | 32 | declare module '*.png' { 33 | const src: string; 34 | export default src; 35 | } 36 | 37 | declare module '*.webp' { 38 | const src: string; 39 | export default src; 40 | } 41 | 42 | declare module '*.svg' { 43 | import * as React from 'react'; 44 | 45 | export const ReactComponent: React.FunctionComponent>; 46 | 47 | const src: string; 48 | export default src; 49 | } 50 | 51 | declare module '*.module.css' { 52 | const classes: { readonly [key: string]: string }; 53 | export default classes; 54 | } 55 | 56 | declare module '*.module.scss' { 57 | const classes: { readonly [key: string]: string }; 58 | export default classes; 59 | } 60 | 61 | declare module '*.module.sass' { 62 | const classes: { readonly [key: string]: string }; 63 | export default classes; 64 | } 65 | 66 | declare module "tone"; 67 | -------------------------------------------------------------------------------- /src/serviceWorker.ts: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | type Config = { 24 | onSuccess?: (registration: ServiceWorkerRegistration) => void; 25 | onUpdate?: (registration: ServiceWorkerRegistration) => void; 26 | }; 27 | 28 | export function register(config?: Config) { 29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 30 | // The URL constructor is available in all browsers that support SW. 31 | const publicUrl = new URL( 32 | process.env.PUBLIC_URL, 33 | window.location.href 34 | ); 35 | if (publicUrl.origin !== window.location.origin) { 36 | // Our service worker won't work if PUBLIC_URL is on a different origin 37 | // from what our page is served on. This might happen if a CDN is used to 38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 39 | return; 40 | } 41 | 42 | window.addEventListener('load', () => { 43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 44 | 45 | if (isLocalhost) { 46 | // This is running on localhost. Let's check if a service worker still exists or not. 47 | checkValidServiceWorker(swUrl, config); 48 | 49 | // Add some additional logging to localhost, pointing developers to the 50 | // service worker/PWA documentation. 51 | navigator.serviceWorker.ready.then(() => { 52 | console.log( 53 | 'This web app is being served cache-first by a service ' + 54 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 55 | ); 56 | }); 57 | } else { 58 | // Is not localhost. Just register service worker 59 | registerValidSW(swUrl, config); 60 | } 61 | }); 62 | } 63 | } 64 | 65 | function registerValidSW(swUrl: string, config?: Config) { 66 | navigator.serviceWorker 67 | .register(swUrl) 68 | .then(registration => { 69 | registration.onupdatefound = () => { 70 | const installingWorker = registration.installing; 71 | if (installingWorker == null) { 72 | return; 73 | } 74 | installingWorker.onstatechange = () => { 75 | if (installingWorker.state === 'installed') { 76 | if (navigator.serviceWorker.controller) { 77 | // At this point, the updated precached content has been fetched, 78 | // but the previous service worker will still serve the older 79 | // content until all client tabs are closed. 80 | console.log( 81 | 'New content is available and will be used when all ' + 82 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 83 | ); 84 | 85 | // Execute callback 86 | if (config && config.onUpdate) { 87 | config.onUpdate(registration); 88 | } 89 | } else { 90 | // At this point, everything has been precached. 91 | // It's the perfect time to display a 92 | // "Content is cached for offline use." message. 93 | console.log('Content is cached for offline use.'); 94 | 95 | // Execute callback 96 | if (config && config.onSuccess) { 97 | config.onSuccess(registration); 98 | } 99 | } 100 | } 101 | }; 102 | }; 103 | }) 104 | .catch(error => { 105 | console.error('Error during service worker registration:', error); 106 | }); 107 | } 108 | 109 | function checkValidServiceWorker(swUrl: string, config?: Config) { 110 | // Check if the service worker can be found. If it can't reload the page. 111 | fetch(swUrl, { 112 | headers: { 'Service-Worker': 'script' } 113 | }) 114 | .then(response => { 115 | // Ensure service worker exists, and that we really are getting a JS file. 116 | const contentType = response.headers.get('content-type'); 117 | if ( 118 | response.status === 404 || 119 | (contentType != null && contentType.indexOf('javascript') === -1) 120 | ) { 121 | // No service worker found. Probably a different app. Reload the page. 122 | navigator.serviceWorker.ready.then(registration => { 123 | registration.unregister().then(() => { 124 | window.location.reload(); 125 | }); 126 | }); 127 | } else { 128 | // Service worker found. Proceed as normal. 129 | registerValidSW(swUrl, config); 130 | } 131 | }) 132 | .catch(() => { 133 | console.log( 134 | 'No internet connection found. App is running in offline mode.' 135 | ); 136 | }); 137 | } 138 | 139 | export function unregister() { 140 | if ('serviceWorker' in navigator) { 141 | navigator.serviceWorker.ready.then(registration => { 142 | registration.unregister(); 143 | }); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | import Instrument from "./instruments/Instrument"; 3 | 4 | export interface INote { 5 | note: string | string[]; 6 | duration: number; 7 | play?: (instrument: Instrument, note, duration, time, velocity) => any; 8 | velocity?: number; 9 | sustain?: number; 10 | display: ts.Node | null; 11 | } 12 | 13 | export interface ITimeNote { 14 | note: string | string[]; 15 | duration: number; 16 | play?: (instrument: Instrument, note, duration, time, velocity) => any; 17 | velocity?: number; 18 | bar?: number; 19 | beat: number; 20 | display: ts.Node | null; 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/calculateMood.ts: -------------------------------------------------------------------------------- 1 | import { max, min } from "lodash"; 2 | import natural from "natural"; 3 | import negativeWords from "../words/negative.json"; 4 | import positiveWords from "../words/positive.json"; 5 | const tokenizer = new natural.WordTokenizer(); 6 | 7 | /* 8 | Moods: 9 | C Major - Happy (more variables) 10 | C Minor - Sad, Love sick (ifs, function declarations) 11 | D Minor - Sad (falsy) 12 | D Major - Triumphant (returns) 13 | Eb Major - Love, Talk to God (globals, windows) 14 | E Major - Ready to fight (try catches) 15 | F Minor - funereal, deep depression (process.exit) 16 | G Major - Fantasy (strings, numbers) 17 | G Minor - Discontent, Uneasiness (throws) 18 | A Major - Declaration of love (imports, exports) 19 | */ 20 | 21 | const octave = 2; 22 | 23 | // const moods = { 24 | // 5: `A${octave} major`, 25 | // 4: `G${octave} major`, 26 | // 3: `Eb${octave} major`, 27 | // 2: `D${octave} major`, 28 | // 1: `E${octave} major`, 29 | // 0: `C${octave} major`, 30 | // "-1": `C${octave} minor`, 31 | // "-2": `D${octave} minor`, 32 | // "-3": `G${octave} minor`, 33 | // "-4": `F${octave} minor` 34 | // }; 35 | 36 | const moods = { 37 | 5: `A${octave} major`, 38 | 4: `G${octave} major`, 39 | 3: `Eb${octave} major`, 40 | 2: `D${octave} major`, 41 | 1: `E${octave} major`, 42 | 0: `C${octave} major`, 43 | "-1": `C${octave} minor`, 44 | "-2": `D${octave} minor`, 45 | "-3": `G${octave} minor`, 46 | "-4": `F${octave} minor` 47 | }; 48 | 49 | const calculateMood = (source: string) => { 50 | const words = tokenizer.tokenize(source); 51 | let positives = 0; 52 | let negatives = 0; 53 | for (const word of words) { 54 | if (positiveWords.includes(word.toLowerCase())) { 55 | positives++; 56 | } else if (negativeWords.includes(word.toLowerCase())) { 57 | negatives++; 58 | } 59 | } 60 | let mood = positives - negatives; 61 | const moods_values = Object.keys(moods).map(k => parseInt(k)); 62 | const maxValue = max(moods_values) as number; 63 | const minValue = min(moods_values) as number; 64 | mood = Math.max(minValue, Math.min(maxValue, mood)); 65 | return moods[mood.toString()]; 66 | }; 67 | 68 | export default calculateMood; 69 | -------------------------------------------------------------------------------- /src/utils/calculateProgression.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | 3 | const progressions = [ 4 | ["I", "V", "VI", "IV"], 5 | ["vi", "IV", "I", "V"], 6 | ["I", "IV", "vi", "V"], 7 | ["I", "VI", "IV", "V"] 8 | ]; 9 | 10 | const calculateProgression = (nodes: ts.Node[]) => { 11 | const blockCount = nodes.filter(node => ts.isBlock(node)).length; 12 | return progressions[blockCount % progressions.length]; 13 | }; 14 | 15 | export default calculateProgression; 16 | -------------------------------------------------------------------------------- /src/utils/createArpeggio.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | import { INote } from "../types"; 3 | 4 | const arpeggios = { 5 | 0: ["1n1,2,3"], 6 | 1: ["2n1s1,2,3", "8n2", "8n3", "8n2", "8n1"], 7 | 2: ["8n4s1", "8n2", "8n4s3", "8n1", "8n4s2", "8n3", "8n4s2", "8n1"], 8 | 3: ["2n1s1,2,3", "4nr", "4n3"], 9 | 4: ["8n2s1", "8n2s2", "8n2s1", "8n2s3", "8n2s2", "8n2s3", "8n2s2", "8n2s1"] 10 | // 4: [ 11 | // "8n0.6v1,2,3", 12 | // "8n0.6v1,2,3", 13 | // "8n0.6v1,2,3", 14 | // "8n0.6v1,2,3", 15 | // "8n0.6v1,2,3", 16 | // "8n0.6v1,2,3", 17 | // "8n0.6v1,2,3", 18 | // "8n0.6v1,2,3" 19 | // ] 20 | }; 21 | 22 | export const calculateArpeggioIndex = (nodes: ts.Node[]) => { 23 | const loopCounts = nodes.filter( 24 | node => 25 | ts.isForStatement(node) || 26 | ts.isForOfStatement(node) || 27 | ts.isForInStatement(node) || 28 | ts.isWhileStatement(node) || 29 | ts.isDoStatement(node) 30 | ).length; 31 | return loopCounts % Object.keys(arpeggios).length; 32 | }; 33 | 34 | const createArpeggio = (chord: string[], arpeggioIndex: number): INote[] => { 35 | const arpeggio = arpeggios[arpeggioIndex]; 36 | const result: INote[] = []; 37 | for (const a of arpeggio) { 38 | const durationMatch = /\d+n/.exec(a); 39 | if (!durationMatch) 40 | throw new Error("No duration in arpeggio notation: " + a); 41 | const duration = parseInt(durationMatch[0]); 42 | const velocityMatch = /\d+(.\d+)?v/.exec(a); 43 | const velocity = velocityMatch ? parseFloat(velocityMatch[0]) : undefined; 44 | const sustainMatch = /\d+s/.exec(a); 45 | const sustain = sustainMatch ? parseInt(sustainMatch[0]) : undefined; 46 | const positionsMatch = /[\d,r]+$/.exec(a); 47 | if (!positionsMatch) 48 | throw new Error("No positions in arpeggio notation: " + a); 49 | const positionsString = positionsMatch[0]; 50 | if (positionsString === "r") { 51 | result.push({ note: "r", duration, display: null }); 52 | } else { 53 | const notes: string[] = []; 54 | const positions = positionsString.split(","); 55 | for (const position of positions) { 56 | notes.push(chord[parseInt(position) - 1]); 57 | } 58 | result.push({ note: notes, duration, velocity, sustain, display: null }); 59 | } 60 | } 61 | return result; 62 | }; 63 | 64 | export default createArpeggio; 65 | -------------------------------------------------------------------------------- /src/utils/getChordHarmonic.ts: -------------------------------------------------------------------------------- 1 | import { note as parseNote } from "@tonaljs/tonal"; 2 | 3 | const getChordHarmonic = (chord: string[]) => { 4 | const notes: string[] = []; 5 | for (let note of chord) { 6 | const parsed = parseNote(note); 7 | notes.push(parsed.pc + "4"); 8 | } 9 | for (let note of chord) { 10 | const parsed = parseNote(note); 11 | notes.push(parsed.pc + "5"); 12 | } 13 | return notes; 14 | }; 15 | 16 | export default getChordHarmonic; 17 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export const mapRange = ( 2 | x: number, 3 | in_min: number, 4 | in_max: number, 5 | out_min: number, 6 | out_max: number 7 | ) => { 8 | return ((x - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min; 9 | }; 10 | 11 | export const mapPower2 = ( 12 | x: number, 13 | in_min: number, 14 | in_max: number, 15 | out_minp2: number, 16 | out_maxp2: number 17 | ) => { 18 | const min_degree = Math.log2(out_minp2); 19 | const max_degree = Math.log2(out_maxp2); 20 | let degree = mapRange(x, in_min, in_max, min_degree, max_degree); 21 | degree = Math.round(degree); 22 | return Math.pow(2, degree); 23 | }; 24 | 25 | export const getKeysWithHighestValue = (o, n) => { 26 | var keys = Object.keys(o); 27 | keys.sort(function(a, b) { 28 | return o[b] - o[a]; 29 | }); 30 | return keys.slice(0, n); 31 | }; 32 | 33 | export const sumOfP2 = (x: number) => { 34 | const result: number[] = []; 35 | let divider = 1; 36 | while (x !== 0) { 37 | if (x >= divider) { 38 | x = x % divider; 39 | if (x === 0) { 40 | result.push(divider); 41 | } else { 42 | result.push(divider); 43 | } 44 | } 45 | divider = divider / 2; 46 | } 47 | return result; 48 | }; 49 | -------------------------------------------------------------------------------- /src/utils/roman.ts: -------------------------------------------------------------------------------- 1 | const roman = str1 => { 2 | if (str1 == null) return -1; 3 | var num = char_to_int(str1.charAt(0)); 4 | var pre, curr; 5 | 6 | for (var i = 1; i < str1.length; i++) { 7 | curr = char_to_int(str1.charAt(i)); 8 | pre = char_to_int(str1.charAt(i - 1)); 9 | if (curr <= pre) { 10 | num += curr; 11 | } else { 12 | num = num - pre * 2 + curr; 13 | } 14 | } 15 | 16 | return num; 17 | }; 18 | 19 | function char_to_int(c) { 20 | switch (c) { 21 | case "I": 22 | return 1; 23 | case "V": 24 | return 5; 25 | case "X": 26 | return 10; 27 | case "L": 28 | return 50; 29 | case "C": 30 | return 100; 31 | case "D": 32 | return 500; 33 | case "M": 34 | return 1000; 35 | default: 36 | return -1; 37 | } 38 | } 39 | export default roman; 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noImplicitAny": false, 16 | "strictPropertyInitialization": false, 17 | "noEmit": true, 18 | "jsx": "react" 19 | }, 20 | "include": ["src"] 21 | } 22 | --------------------------------------------------------------------------------