diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6da5c03 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Verwendet IntelliSense zum Ermitteln möglicher Node.js-Debugattribute. + // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen. + // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Programm starten", + "program": "${workspaceRoot}/lib/timec.js", + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c5d78b2 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "tsc", + "isShellCommand": true, + "args": ["-w", "-p", "."], + "showOutput": "silent", + "isBackground": true, + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/concept.md b/concept.md new file mode 100644 index 0000000..e69de29 diff --git a/db.json b/db.json index 9ba60fa..f722eb7 100644 --- a/db.json +++ b/db.json @@ -8,5 +8,25 @@ "name": "test", "value": "5" } - ] + ], + "3d47edcdc882": { + "displayName": "test", + "id": "3d47edcdc882", + "triggers": [ + { + "midi": { + "channel": 1, + "note": 3, + "value": 4 + }, + "time": { + "lframe": 15, + "lseconds": 5, + "lminutes": 0, + "lhours": 0, + "lmilliseconds": 5600 + } + } + ] + } } \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index bfeb3ab..0000000 --- a/index.js +++ /dev/null @@ -1,186 +0,0 @@ -var low = require("lowdb"); -var db = low("db.json"); -var sid = require("short-id"); -sid.configure({length:12}); -db.defaults({}).write(); - -var http = require("http").createServer(); - -var ioClient = require("socket.io-client")("http://localhost:5000"); -var io = require("socket.io")(http); - -var activeFile = undefined; -var toTrigger = []; - -class FrameTime{ - constructor(millis){ - this.lframe = 0; - this.lseconds = 0; - this.lminutes = 0; - this.lhours = 0; - this.lmilliseconds = 0; - if(Number.isInteger(millis)){ - this.milliseconds = millis; - } else if(typeof millis === "object"){ - if(Number.isInteger(millis.frame) && Number.isInteger(millis.seconds) && Number.isInteger(millis.minutes) && Number.isInteger(millis.hours) ){ - this.lframe = millis.frame; //use the local, because its mor performant - this.lseconds = milllis.seconds; - this.lminutes = millis.minutes; - this.lhours = millis.hours; - this.calcMillis(); - } else if(Number.isInteger(millis.milliseconds)){ - this.milliseconds = millis.milliseconds; - } - } - } - - get milliseconds() { - return this.lmilliseconds; - } - - set milliseconds(val) { - this.lmilliseconds = val; - millisToFrame(); - } - - get frame(){ - return this.lframe; - } - - set frame (val) { - this.lframe = val; - calcMillis(); - } - - get seconds() { - return this.lseconds; - } - - set seconds(val){ - this.lseconds = val; - calcMillis(); - } - - get minutes() { - return this.lminutes; - } - - set minutes(val) { - this.lminutes = val; - calcMillis(); - } - - get hours(){ - return this.lhours; - } - - set hours(val){ - this.lhours = val; - calcMillis(); - } - - calcMillis(){ - this.lmilliseconds = (this.frame / 25 * 1000) + this.seconds * 1000 + this.minutes * 60 * 1000 + this.hours * 60 * 60 * 1000; - } - - millisToFrame(millis) { - this.frame = Math.floor((millis % 1000)*25/1000); - this.seconds = Math.floor((millis / 1000) % 60); - this.minutes = Math.floor((millis / (1000*60)) % 60); - this.hours = Math.floor((millis / (1000*60*60)) % 24); - } -} - -ioClient.on("update", (data)=>{ - activeFile = data.file; - var t = data.time; - var ft = new FrameTime(t); - var answer = {}; - answer.time = ft; - answer.timemillis = t; - answer.file = activeFile; - answer.playing = data.playing; - io.emit("update", answer); - -}); - -ioClient.on("playing", (data)=>{ - io.emit("playing", data); -}); - -ioClient.on("stopped", ()=>{ - io.emit("stopped") -}); - -io.on("connection", (socket)=>{ - //console.log("client connected:", socket.client.id); - socket.on("set", (data)=>{ //ToDo preset ids etc. - console.log(data); - var entry = {}; - entry.id = sid.generate(); - entry.time = new FrameTime(data.time); //typof FrameTime - entry.channel = data.channel; - entry.note = data.note; - entry.name = data.name; - data.name = data.name.replace(".", ""); - entry.value = data.value; - if(db.get(data.name).value() === undefined) { - db.set(data.name, []).write(); - } - db.get(data.name).push(entry).write(); - }); - - socket.on("get", (data)=>{ //Requires filename - var v = db.get(data.replace(".", "")).value(); - socket.emit("data", v); - }); - - socket.on("remove", (data)=>{ //requires id - db.get(data.replace(".", "")).remove({id:data}).write(); - }); - - socket.on("load_preset", (data)=>{ - toTrigger = db.get(data.replace(".", "")).value(); - }); - - socket.on("play", (data)=>{ - ioClient.emit("play", data); - }); - - socket.on("stop", (data)=>{ - ioClient.emit("stop", data); - }); -}); - -setInterval(()=>{ - var start - var update_infos = {}; - player.getOrigTime((time)=>{ - player.getTime((t)=>{ - player.getState((playing)=>{ - update_infos.file = activeFile; - update_infos.time = t; - update_infos.is_playing = playing; - update_infos.triggeredMidi = []; - if(playing){ - if(toTrigger){ - toTrigger.forEach(e=>{ - if(e.time <= t){ - var midi = {channel:e.channel, note:e.note, value:e.value} - io.emit("midi", {channel:e.channel, note:e.note, value:e.value}); - var i = toTrigger.indexOf(e); - if (i < 0) return; - toTrigger.splice(i, 1); - update_infos.triggeredMidi.push(midi); - } - }); - } - } - io.emit("update", update_infos); - //console.log(JSON.stringify(update_infos, undefined, 4)); - }) - }); - }); -}, 1000/25) - -http.listen(5000); \ No newline at end of file diff --git a/lib/db.json b/lib/db.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/lib/db.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/lib/timec.d.ts b/lib/timec.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/lib/timec.js b/lib/timec.js new file mode 100644 index 0000000..487b681 --- /dev/null +++ b/lib/timec.js @@ -0,0 +1,197 @@ +//DATABASE +"use strict"; +const low = require("lowdb"); +var db = low("db.json"); +const sid = require("short-id"); +sid.configure({ length: 12 }); +db.defaults({}).write(); +//SOCKETIO +const httpp = require("http"); +var http = httpp.createServer(); +const io = require("socket.io"); +var ioClient = require("socket.io-client")("http://localhost:5000"); +var ios = io(http); +const rtpmidi = require("rtpmidi"); +var midisession = rtpmidi.manager.createSession({ + localName: "Node Timecode", + bonjourName: "Node Timecode", + port: 5004 +}); +class FrameTime { + constructor(millis) { + this.lframe = 0; + this.lseconds = 0; + this.lminutes = 0; + this.lhours = 0; + this.lmilliseconds = 0; + if (Number.isInteger(millis)) { + this.milliseconds = millis; + } + else if (typeof millis === "object") { + if (Number.isInteger(millis.frames) && Number.isInteger(millis.seconds) && Number.isInteger(millis.minutes) && Number.isInteger(millis.hours)) { + this.lframe = millis.frames; //use the local, because its mor performant + this.lseconds = millis.seconds; + this.lminutes = millis.minutes; + this.lhours = millis.hours; + this.calcMillis(); + } + else if (Number.isInteger(millis.milliseconds)) { + this.milliseconds = millis.milliseconds; + } + } + } + get milliseconds() { + return this.lmilliseconds; + } + set milliseconds(val) { + this.lmilliseconds = val; + this.millisToFrame(); + } + get frame() { + return this.lframe; + } + set frame(val) { + this.lframe = val; + this.calcMillis(); + } + get seconds() { + return this.lseconds; + } + set seconds(val) { + this.lseconds = val; + this.calcMillis(); + } + get minutes() { + return this.lminutes; + } + set minutes(val) { + this.lminutes = val; + this.calcMillis(); + } + get hours() { + return this.lhours; + } + set hours(val) { + this.lhours = val; + this.calcMillis(); + } + calcMillis() { + this.lmilliseconds = (this.frame / 25 * 1000) + this.seconds * 1000 + this.minutes * 60 * 1000 + this.hours * 60 * 60 * 1000; + } + millisToFrame() { + var millis = this.lmilliseconds; + this.frame = Math.floor((millis % 1000) * 25 / 1000); + this.seconds = Math.floor((millis / 1000) % 60); + this.minutes = Math.floor((millis / (1000 * 60)) % 60); + this.hours = Math.floor((millis / (1000 * 60 * 60)) % 24); + } +} +class Timecode { + vlcUpdate(vlc) { + this.startTime = new Date().getTime() - vlc.time; + this.filePlaying = vlc.file; + } + update(time) { + var d = new Date().getTime() - this.startTime; + this.toTrigger.forEach(e => { + if (e.time.milliseconds <= d) { + ios.emit("midi", e.midi); + midisession.sendMessage([e.midi.channel, e.midi.note, e.midi.value]); + var i = this.toTrigger.indexOf(e); + this.toTrigger.splice(i, 1); + } + }); + } + loadTrack(trackid) { + this.playing = false; + var track = db.get(trackid).value(); + track.triggers.forEach(t => { + t.time = new FrameTime(t.time); + }); + this.activeTrack = track; + this.toTrigger = track.triggers; + this.filePlaying = track.file; + this.startTime = 0; + } + play() { + if (this.filePlaying) { + ioClient.emit("play", { file: this.filePlaying }); + ioClient.on("playing", () => { + this.startTime = new Date().getTime(); + }); + ioClient.on("update", (data) => { + this.vlcUpdate(data); + }); + } + else { + this.startTime = new Date().getTime(); + this.playing = true; + } + this.interval = setInterval(this.update.bind(this), 1000 / 25); + } + stop() { + clearInterval(this.interval); + this.playing = false; + if (this.filePlaying) { + ioClient.emit("stop"); + ios.emit("stopped"); + } + this.startTime = 0; + } +} +class TrackEvent { +} +class Track { +} +var timecode = new Timecode(); +ios.on("connection", socket => { + console.log("Client", socket.id, "connected"); + socket.on("load", (id) => { + var track = db.get(id).value(); + if (track === undefined) { + return socket.emit("play_error", "Track doesn't exist"); + } + timecode.loadTrack(id); + }); + socket.on("play", () => { + if (timecode.playing) { + return socket.emit("play_error", "the active track must be stoppen before re-plaing it"); + } + timecode.play(); + }); + socket.on("stop", () => { + timecode.stop(); + timecode.loadTrack(timecode.activeTrack.id); + }); + socket.on("create", (data) => { + var track = new Track(); + track.displayName = data.name; + track.file = data.file; + track.id = sid.generate(); + track.triggers = []; + db.set(track.id, track).write(); + socket.emit("created", track); + }); + socket.on("set", (data) => { + var tid = data.track; + var midi = data.midi; + var time = new FrameTime(data.time); + var tracke = new TrackEvent(); + tracke.midi = midi; + tracke.time = time; + db.get(tid + ".triggers").push(tracke).write(); + socket.emit("set_finished", tracke); + }); + socket.on("get", () => { + socket.emit("get_data", db.getState()); + }); +}); +midisession.on("ready", () => { + http.listen(5001); + setInterval(() => { + midisession.sendMessage([0x80, 0x40]); + midisession.sendMessage([0x90, 0x40, 0x7f]); + //midisession.sendMessage([1, 5, 100]); + }, 500); +}); +//# sourceMappingURL=timec.js.map \ No newline at end of file diff --git a/lib/timec.js.map b/lib/timec.js.map new file mode 100644 index 0000000..05a4709 --- /dev/null +++ b/lib/timec.js.map @@ -0,0 +1 @@ +{"version":3,"file":"timec.js","sourceRoot":"","sources":["../src/timec.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 82e5f2c..46544a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3,6 +3,21 @@ "version": "0.0.1", "lockfileVersion": 1, "dependencies": { + "@types/node": { + "version": "7.0.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.31.tgz", + "integrity": "sha512-+KrE1LDddn97ip+gXZAnzNQ0pupKH/6tcKwTpo96BDVNpzmhIKGHug0Wd3H0dN4WEqYB1tXYI5m2mZuIZNI8tg==" + }, + "@types/socket.io": { + "version": "1.4.29", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-1.4.29.tgz", + "integrity": "sha1-hqazqat4z5qQDO74W5totr6oZxI=" + }, + "@types/socket.io-client": { + "version": "1.4.29", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.29.tgz", + "integrity": "sha1-+HQwcM7pMXXjbgtqd6ivc+WMyzI=" + }, "accepts": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", @@ -138,6 +153,11 @@ "resolved": "https://registry.npmjs.org/merge-recursive/-/merge-recursive-0.0.3.tgz", "integrity": "sha1-3nkB78rsyQbYyrKtHpxHD1o9roQ=" }, + "midi-common": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/midi-common/-/midi-common-0.0.4.tgz", + "integrity": "sha1-OC6/3wXh4oMAutER6a1fB6gf7HI=" + }, "mime-db": { "version": "1.27.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", @@ -188,6 +208,11 @@ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=" }, + "rtpmidi": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/rtpmidi/-/rtpmidi-0.1.8.tgz", + "integrity": "sha1-deRjzVueLhlzBhXtsKlM8TwuxH4=" + }, "safe-buffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", diff --git a/package.json b/package.json index fb83f58..8d9eede 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "test": "node test.js", - "start": "node index.js" + "start": "node lib/index.js" }, "repository": { "type": "git", @@ -26,7 +26,11 @@ }, "homepage": "https://github.com/margau/node_vlc_rc#readme", "dependencies": { + "@types/node": "^7.0.31", + "@types/socket.io": "^1.4.29", + "@types/socket.io-client": "^1.4.29", "lowdb": "^0.16.2", + "rtpmidi": "^0.1.8", "short-id": "0.1.0-1", "socket.io": "^2.0.3", "socket.io-client": "^2.0.3" diff --git a/src/timec.ts b/src/timec.ts new file mode 100644 index 0000000..c2eae0b --- /dev/null +++ b/src/timec.ts @@ -0,0 +1,243 @@ +//DATABASE + +import low = require("lowdb"); +var db = low("db.json"); +import sid = require("short-id"); +sid.configure({length:12}); +db.defaults({}).write(); + +//SOCKETIO +import * as httpp from "http"; +var http = httpp.createServer(); +import io = require("socket.io"); +var ioClient = require("socket.io-client")("http://localhost:5000"); +var ios = io(http); + +import rtpmidi = require("rtpmidi"); +var midisession = rtpmidi.manager.createSession({ + localName:"Node Timecode", + bonjourName:"Node Timecode", + port: 5004 +}); + +class FrameTime{ + lframe = 0; + lseconds = 0; + lminutes = 0; + lhours = 0; + lmilliseconds = 0; + constructor(millis){ + if(Number.isInteger(millis)){ + this.milliseconds = millis; + } else if(typeof millis === "object"){ + if(Number.isInteger(millis.frames) && Number.isInteger(millis.seconds) && Number.isInteger(millis.minutes) && Number.isInteger(millis.hours) ){ + this.lframe = millis.frames; //use the local, because its mor performant + this.lseconds = millis.seconds; + this.lminutes = millis.minutes; + this.lhours = millis.hours; + this.calcMillis(); + } else if(Number.isInteger(millis.milliseconds)){ + this.milliseconds = millis.milliseconds; + } + } + } + + get milliseconds() { + return this.lmilliseconds; + } + + set milliseconds(val) { + this.lmilliseconds = val; + this.millisToFrame(); + } + + get frame(){ + return this.lframe; + } + + set frame (val) { + this.lframe = val; + this.calcMillis(); + } + + get seconds() { + return this.lseconds; + } + + set seconds(val){ + this.lseconds = val; + this.calcMillis(); + } + + get minutes() { + return this.lminutes; + } + + set minutes(val) { + this.lminutes = val; + this.calcMillis(); + } + + get hours(){ + return this.lhours; + } + + set hours(val){ + this.lhours = val; + this.calcMillis(); + } + + calcMillis(){ + this.lmilliseconds = (this.frame / 25 * 1000) + this.seconds * 1000 + this.minutes * 60 * 1000 + this.hours * 60 * 60 * 1000; + } + + millisToFrame() { + var millis = this.lmilliseconds; + this.frame = Math.floor((millis % 1000)*25/1000); + this.seconds = Math.floor((millis / 1000) % 60); + this.minutes = Math.floor((millis / (1000*60)) % 60); + this.hours = Math.floor((millis / (1000*60*60)) % 24); + } + +} + +class Timecode { + interval; + playing:boolean; + activeTrack:Track; + filePlaying:string; + startTime:number; + toTrigger:Array; + + vlcUpdate(vlc:VlcUpdate) { + this.startTime = new Date().getTime() - vlc.time; + this.filePlaying = vlc.file; + } + + update(time:FrameTime) { + var d = new Date().getTime() - this.startTime; + this.toTrigger.forEach(e=>{ + if(e.time.milliseconds <= d) { + ios.emit("midi", e.midi); + midisession.sendMessage([e.midi.channel, e.midi.note, e.midi.value]); + var i = this.toTrigger.indexOf(e); + this.toTrigger.splice(i, 1); + } + }); + } + + loadTrack(trackid:string) { + this.playing = false; + var track =db.get(trackid).value(); + track.triggers.forEach(t=>{ + t.time = new FrameTime(t.time); + }); + this.activeTrack = track; + this.toTrigger = track.triggers; + this.filePlaying = track.file; + this.startTime = 0; + } + + play() { + if(this.filePlaying){ + ioClient.emit("play", {file:this.filePlaying}); + ioClient.on("playing", ()=>{ + this.startTime = new Date().getTime(); + }); + ioClient.on("update", (data)=>{ + this.vlcUpdate(data); + }) + } else { + this.startTime = new Date().getTime(); + this.playing = true; + } + + this.interval = setInterval(this.update.bind(this), 1000/25); + } + + stop() { + clearInterval(this.interval); + this.playing = false; + if(this.filePlaying){ + ioClient.emit("stop"); + ios.emit("stopped"); + } + this.startTime = 0; + } +} + +class TrackEvent { + midi: { + channel:number, + note:number, + value:number + } + time:FrameTime; +} + +class Track { + id:string + displayName:string; + triggers:Array; + file:string; +} + +interface VlcUpdate { + file:string, + time:number, + is_playing:boolean; +} + +var timecode = new Timecode(); +ios.on("connection", socket=>{ + console.log("Client", socket.id, "connected"); + socket.on("load", (id)=>{ + var track = db.get(id).value(); + if(track === undefined){ + return socket.emit("play_error", "Track doesn't exist"); + } + timecode.loadTrack(id); + }); + socket.on("play", ()=>{ + if(timecode.playing){ + return socket.emit("play_error", "the active track must be stoppen before re-plaing it"); + } + timecode.play(); + }); + socket.on("stop", ()=>{ + timecode.stop(); + timecode.loadTrack(timecode.activeTrack.id); + }); + socket.on("create", (data)=>{ + var track = new Track(); + track.displayName = data.name; + track.file = data.file; + track.id = sid.generate(); + track.triggers = []; + db.set(track.id, track).write(); + socket.emit("created", track); + }); + socket.on("set", (data)=>{ + var tid = data.track; + var midi = data.midi; + var time = new FrameTime(data.time); + var tracke = new TrackEvent(); + tracke.midi = midi; + tracke.time = time; + db.get(tid + ".triggers").push(tracke).write(); + socket.emit("set_finished", tracke); + }); + + socket.on("get", ()=>{ + socket.emit("get_data", db.getState()); + }); +}); + +midisession.on("ready", ()=>{ + http.listen(5001); + setInterval(()=>{ + midisession.sendMessage([0x80, 0x40]); + midisession.sendMessage([0x90, 0x40, 0x7f]); + //midisession.sendMessage([1, 5, 100]); + }, 500); +}); \ No newline at end of file diff --git a/testEvent.json b/testEvent.json new file mode 100644 index 0000000..61e5ded --- /dev/null +++ b/testEvent.json @@ -0,0 +1,13 @@ +{ + "track": "3d47edcdc882", + "midi":{ + "channel":1, + "note":3, + "value":4 + }, "time": { + "hours":0, + "minutes": 0, + "seconds":5, + "frames": 15 + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d7a9286 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "noImplicitAny": false, + "sourceMap": true, + "outDir": "lib", + "declaration": true, + "typeRoots": [ + "node_modules/@types" + ] + }, + "files": [ + "src/custom.d.ts" + ], + "exclude": [ + "node_modules" + ],"include": [ + "src" + ] +} \ No newline at end of file