Last active
September 23, 2020 07:02
-
-
Save sebm/cbd64e30e21edd7049a9e5a7cd62a2fe to your computer and use it in GitHub Desktop.
Revisions
-
sebm revised this gist
Sep 23, 2020 . 1 changed file with 8 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,8 @@ exports.handler = function(context, event, callback) { const twiml = new Twilio.twiml.VoiceResponse(); const { bpm } = event; twiml.play(`💃.cloudfunctions.net/metronome?bpm=${bpm}`) return callback(null, twiml); }; -
sebm revised this gist
Sep 23, 2020 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ // Google Cloud Source repos don't allow world-readable so this is the next best thing // this is the code as of SHA b34717ce19fa9da940333402e7534c0e99f687a3 const synth = require("synth-js"); const MidiWriter = require("midi-writer-js"); const ffmpeg = require("fluent-ffmpeg"); -
sebm created this gist
Sep 23, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,92 @@ const synth = require("synth-js"); const MidiWriter = require("midi-writer-js"); const ffmpeg = require("fluent-ffmpeg"); const { Readable } = require("stream"); const concat = require("concat-stream"); const MEASURES = 100; const THE_ONE = { pitch: ["A6"], duration: "16" }; const TWO_THREE_FOUR = { pitch: ["A4"], duration: "16", wait: ["16", "16", "16"], repeat: "3", }; const MAX_BPM = 400; function validateRequest(req) { const { bpm } = req.query; if (!bpm) { throw new Error("bpm param must be set"); } if (Number.isNaN(Number.parseInt(bpm))) { throw new Error("bpm must be a number"); } if (bpm > MAX_BPM) { throw new Error(`bpm must be less than ${MAX_BPM}`); } if (bpm < 40) { throw new Error("bpm must be less than 40"); } } exports.metronome = (req, res) => { try { validateRequest(req); // Start with a new track const track = new MidiWriter.Track(); const { bpm } = req.query; // Define an instrument (optional): track.addEvent(new MidiWriter.ProgramChangeEvent({ instrument: 1 })); track.setTempo(bpm); track.addEvent( [ new MidiWriter.NoteEvent(THE_ONE), new MidiWriter.NoteEvent(TWO_THREE_FOUR), ], () => ({ sequential: true }) ); for (let x = 0; x < MEASURES; x++) { track.addEvent( [ new MidiWriter.NoteEvent({ ...THE_ONE, wait: ["16", "16", "16"] }), new MidiWriter.NoteEvent(TWO_THREE_FOUR), ], () => ({ sequential: true }) ); } const midiWriter = new MidiWriter.Writer(track); const midiBuffer = new Buffer.from(midiWriter.buildFile()); const wavBuffer = synth.midiToWav(midiBuffer).toBuffer(); const metronomeWavStream = Readable.from(wavBuffer); const filename = `${bpm}.mp3`; res.setHeader("Content-disposition", "attachment; filename=" + filename); res.contentType("mp3"); const concatStream = concat((buffer) => { res.send(buffer); res.end(); }); ffmpeg() .format("mp3") .audioCodec("libmp3lame") .audioBitrate(8) .audioFrequency(12000) .input(metronomeWavStream) .pipe(concatStream); } catch (err) { res.status(500).send(err.message); } };