Skip to content

Commit

Permalink
Merge pull request #93 from magenta/bug/multiple-instances
Browse files Browse the repository at this point in the history
Fix bug where multiple instances can't be open
  • Loading branch information
catarak authored Jun 29, 2023
2 parents 5146389 + 6219bc8 commit 30bca26
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 97 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ TODO.txt
modelz

magenta4live.amxd/code/public/
magenta4live.amxd/magenta4live.amxd

This comment has been minimized.

Copy link
@KetLatch

KetLatch Sep 17, 2024

good maker
hihihiihihihih

This comment has been minimized.

Copy link
@KetLatch

KetLatch Sep 17, 2024

im sorry

magenta4live.amxd/magenta4live.amxd
*.amxd
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This is not a Google product.

Magenta Studio is a set of plugins contained in a [Max for Live](https://www.ableton.com/en/live/max-for-live/) Device. They are contained in a single web application that runs in the Max environment via Chromium Embedded Framework (CEF). All of the front-end code is contained in the `client/` folder, which contains the five plugins, and common UI and communication components which can be found in the `client/components/` folder. These objects are built using [lit](https://lit.dev/).

The communication between the web application and Live is handed through Max. This contains a local express server that runs on port 3333 in [Node for Max](https://cycling74.com/articles/node-for-max-intro-%E2%80%93-let%E2%80%99s-get-started). The Max patch and related JavaScript files can be found in the folder called `magenta4live.amxd/`.
The communication between the web application and Live is handed through Max. This contains a local express server that runs on port 3333 (or 3334, 3335, etc if you have multiple instances open) in [Node for Max](https://cycling74.com/articles/node-for-max-intro-%E2%80%93-let%E2%80%99s-get-started). The Max patch and related JavaScript files can be found in the folder called `magenta4live.amxd/`.

## Installation

Expand All @@ -26,7 +26,7 @@ To run the front-end web application in development mode, in which webpack will
npm run watch
```

You will need to open the Max project to start the express server to test your changes. You can either view the app in the [`jweb`] object in Max, or load it in the browser at [http://localhost:3333](http://localhost:3333). Hot reloading is not enabled, so you will need to refresh to see changes.
You will need to open the Max project to start the express server to test your changes. You can either view the app in the [`jweb`] object in Max, or load it in the browser at [http://localhost:3333](http://localhost:3333) (again, 3334, 3335, etc. if there are multiple instances running). Hot reloading is not enabled, so you will need to refresh to see changes.

## Building

Expand Down
12 changes: 6 additions & 6 deletions client/about/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import './style.scss'
// https://magenta.tensorflow.org/studio/ in the default browser.
// In CEF, you can't open links in the default browser, but it is
// possible to do this in Max.
function handleOpenWebsite(e){
e.preventDefault()
fetch(e.target.href)
function handleOpenWebsite(e) {
e.preventDefault()
fetch(e.target.href)
}

export function About(parentElement){
render(html`
export function About(parentElement) {
render(html`
<div class="about">
<h2 id="title">Magenta Studio</h2>
<center><p>Version: ${VERSION}</p></center>
Expand All @@ -21,7 +21,7 @@ export function About(parentElement){
</p>
<p>
Find more information and tutorials at
<a target="_blank" href="http://localhost:3333/studio" @click=${handleOpenWebsite}>our website.</a>
<a target="_blank" href="/studio" @click=${handleOpenWebsite}>our website.</a>
</p>
<!--
<p>
Expand Down
48 changes: 24 additions & 24 deletions client/components/src/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,31 @@
* limitations under the License.
*/

const ROUTE = 'http://localhost:3333'
// const ROUTE = 'http://localhost:3333'
import { encode, decode } from '../../../magenta4live.amxd/code/src/Notes'

async function GET(path){
return await fetch(`${ROUTE}/${path}`)
async function GET(path) {
return await fetch(`/${path}`)
}

async function POST(path, body){
return await fetch(`${ROUTE}/${path}`, {
method : 'POST',
body : JSON.stringify(body),
headers : {
Accept : 'application/json',
'Content-Type' : 'application/json'
async function POST(path, body) {
return await fetch(`/${path}`, {
method: 'POST',
body: JSON.stringify(body),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
}

async function CALL(method, args){
const response = await fetch(`${ROUTE}/call`, {
method : 'POST',
body : JSON.stringify({ method, args }),
headers : {
Accept : 'application/json',
'Content-Type' : 'application/json'
async function CALL(method, args) {
const response = await fetch(`/call`, {
method: 'POST',
body: JSON.stringify({ method, args }),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
return await response.json()
Expand All @@ -48,22 +48,22 @@ async function CALL(method, args){
/**
* Returns true if it's connected to Live
*/
export async function connected(){
export async function connected() {
try {
await GET('status')
return true
} catch (e){
} catch (e) {
return false
}
}

export async function setNotes(args){
export async function setNotes(args) {
args.notes = encode(args.notes)
const response = await CALL('set_notes', args)
return response
}

export async function getNotes(id){
export async function getNotes(id) {
const response = await CALL('get_notes', { id })
response.notes = decode(response.notes)
return response
Expand All @@ -72,20 +72,20 @@ export async function getNotes(id){
/**
* Get all the tracks
*/
export async function tracks(){
export async function tracks() {
return await CALL('tracks')
}

/**
* Get all the clips in a track
*/
export async function clips(id){
export async function clips(id) {
return await CALL('clips', { id })
}

/**
* Make sure a track has at least this many clips
*/
export async function setTrackLength(id, length){
export async function setTrackLength(id, length) {
return await CALL('set_track_length', { length, id })
}
31 changes: 15 additions & 16 deletions client/interpolate/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,51 +19,50 @@ import { MusicVAE, sequences } from '@magenta/music'
const { quantizeNoteSequence, unquantizeSequence, clone } = sequences

export class Model {
constructor(drums=false){
constructor(drums = false) {
// const melodyUrl = 'https://storage.googleapis.com/magentadata/js/checkpoints/music_vae/mel_4bar_med_q2'
// const drumsUrl = 'https://storage.googleapis.com/magentadata/js/checkpoints/music_vae/drums_4bar_med_q2'
const drumUrl = '/interpolate/models/drums_4bar_med'
const melodyUrl = '/interpolate/models/mel_4bar_med'
this.model = new MusicVAE(drums ? drumUrl : melodyUrl)
}

async load(){
async load() {
try {
await this.model.initialize()
} catch (e){
} catch (e) {
const snackbar = document.createElement('magenta-snackbar')
document.body.appendChild(snackbar)
}
}

validateSequence(seqA, seqB){
validateSequence(seqA, seqB) {
const maxBeats = 4 * 16
//make sure they're the same number of measures
const len = Math.min(seqA.totalQuantizedSteps, seqB.totalQuantizedSteps, maxBeats)
this.trim(seqA, len)
this.trim(seqB, len)
}

trim(sequence, beats){
trim(sequence, beats) {
sequence.totalQuantizedSteps = beats
sequence.totalTime = beats * 0.25
sequence.notes = sequence.notes.filter(n => n.quantizedEndStep < beats)
console.log(sequence)
}

trimOutput(inSequence, outSequences){
trimOutput(inSequence, outSequences) {
outSequences.forEach(seq => {
this.trim(seq, inSequence.totalQuantizedSteps)
})
}

setVelocities(sequences){
setVelocities(sequences) {
sequences.forEach(seq => {
seq.notes.forEach(n => n.velocity = 100)
})
}

async predict(inputSequenceA, inputSequenceB, steps=4, temp=1){
async predict(inputSequenceA, inputSequenceB, steps = 4, temp = 1) {
const quarterNoteSubdiv = 4
inputSequenceA = quantizeNoteSequence(inputSequenceA, quarterNoteSubdiv)
inputSequenceB = quantizeNoteSequence(inputSequenceB, quarterNoteSubdiv)
Expand All @@ -75,22 +74,22 @@ export class Model {
return outSequences
}

concat(...args){
if (args.length === 2){
concat(...args) {
if (args.length === 2) {
const [seqA, seqB] = args
const outputSequence = clone(seqA)
seqB.notes.forEach(note => {
const clonedNote = Object.assign({}, note)
clonedNote.startTime += outputSequence.totalQuantizedSteps/4
clonedNote.endTime += outputSequence.totalQuantizedSteps/4
clonedNote.startTime += outputSequence.totalQuantizedSteps / 4
clonedNote.endTime += outputSequence.totalQuantizedSteps / 4
clonedNote.quantizedStartStep += seqA.totalQuantizedSteps
clonedNote.quantizedEndStep += seqA.totalQuantizedSteps
outputSequence.notes.push(clonedNote)
})
outputSequence.totalQuantizedSteps = seqA.totalQuantizedSteps + seqB.totalQuantizedSteps
outputSequence.totalTime = outputSequence.totalQuantizedSteps / 4
return outputSequence
} else if (args.length > 2){
} else if (args.length > 2) {
//concat the last two
//get the first arg
const first = args.shift()
Expand All @@ -100,10 +99,10 @@ export class Model {
}
}

duplicate(seqs, repeats){
duplicate(seqs, repeats) {
const outSeq = []
seqs.forEach(s => {
for (let i = 0; i < repeats; i++){
for (let i = 0; i < repeats; i++) {
outSeq.push(s)
}
})
Expand Down
48 changes: 28 additions & 20 deletions magenta4live.amxd/code/magenta-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,24 @@ const max = require('max-api')
// CONFIG
///////////////////////////////////////////////////////////////////////////////

const PORT = 3333
let PORT = 3333
app.use(express.static('./public'))
app.get('/continue', function(req, res){
res.sendFile('./public/index.html', { root : __dirname })
app.get('/continue', function (req, res) {
res.sendFile('./public/index.html', { root: __dirname })
})
app.get('/drumify', function(req, res){
res.sendFile('./public/index.html', { root : __dirname })
app.get('/drumify', function (req, res) {
res.sendFile('./public/index.html', { root: __dirname })
})
app.get('/generate', function(req, res){
res.sendFile('./public/index.html', { root : __dirname })
app.get('/generate', function (req, res) {
res.sendFile('./public/index.html', { root: __dirname })
})
app.get('/groove', function(req, res){
res.sendFile('./public/index.html', { root : __dirname })
app.get('/groove', function (req, res) {
res.sendFile('./public/index.html', { root: __dirname })
})
app.get('/interpolate', function(req, res){
res.sendFile('./public/index.html', { root : __dirname })
app.get('/interpolate', function (req, res) {
res.sendFile('./public/index.html', { root: __dirname })
})
app.get('/studio', function(req, res){
app.get('/studio', function (req, res) {
max.outlet('openWebsite')
res.send('success')
})
Expand All @@ -53,7 +53,7 @@ app.get('/studio', function(req, res){
// ROUTES
///////////////////////////////////////////////////////////////////////////////

async function getId(path){
async function getId(path) {
const id = await outlet('path', decodeURIComponent(path))
return parseInt(id)
}
Expand All @@ -78,27 +78,35 @@ app.post('/call', async (req, res) => {

let server = null

async function startServer(){
async function startServer() {
const availPort = await detect(PORT)
if (PORT !== availPort){
if (PORT !== availPort) {
//try and kill the port, and then try again
console.log(`killing server on ${PORT}`)
await kill(PORT)
// console.log(`killing server on ${PORT}`)
// await kill(PORT)
//try again
PORT = PORT + 1
max.outlet('server', 0)
setTimeout(() => startServer(), 1000)
// setTimeout(() => startServer(), 1000)
setTimeout(() => startServer(), 1)
} else {
server = app.listen(PORT, () => {
max.outlet('server', 1)
max.outlet('server', 1, PORT)
console.log(`server started on ${PORT}`)
})
}
}

startServer()

max.addHandler("closing", () => {
if (server) {
server.close()
}
});

process.on('exit', () => {
if (server){
if (server) {
server.close()
}
})
Expand Down
1 change: 1 addition & 0 deletions magenta4live.amxd/code/magenta-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function out(responseId, data){
*/
function remote_invoke(method, args, responseId){
try {
post("received recponseId " + responseId + "\n");
args = JSON.parse(args)
var response = this[method](args)
out(responseId, response)
Expand Down
Loading

0 comments on commit 30bca26

Please sign in to comment.