Skip to content

Commit 30bca26

Browse files
authored
Merge pull request #93 from magenta/bug/multiple-instances
Fix bug where multiple instances can't be open
2 parents 5146389 + 6219bc8 commit 30bca26

9 files changed

Lines changed: 204 additions & 97 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,5 @@ TODO.txt
7474
modelz
7575

7676
magenta4live.amxd/code/public/
77-
magenta4live.amxd/magenta4live.amxd
77+
magenta4live.amxd/magenta4live.amxd
78+
*.amxd

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This is not a Google product.
66

77
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/).
88

9-
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/`.
9+
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/`.
1010

1111
## Installation
1212

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

29-
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.
29+
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.
3030

3131
## Building
3232

client/about/index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import './style.scss'
55
// https://magenta.tensorflow.org/studio/ in the default browser.
66
// In CEF, you can't open links in the default browser, but it is
77
// possible to do this in Max.
8-
function handleOpenWebsite(e){
9-
e.preventDefault()
10-
fetch(e.target.href)
8+
function handleOpenWebsite(e) {
9+
e.preventDefault()
10+
fetch(e.target.href)
1111
}
1212

13-
export function About(parentElement){
14-
render(html`
13+
export function About(parentElement) {
14+
render(html`
1515
<div class="about">
1616
<h2 id="title">Magenta Studio</h2>
1717
<center><p>Version: ${VERSION}</p></center>
@@ -21,7 +21,7 @@ export function About(parentElement){
2121
</p>
2222
<p>
2323
Find more information and tutorials at
24-
<a target="_blank" href="http://localhost:3333/studio" @click=${handleOpenWebsite}>our website.</a>
24+
<a target="_blank" href="/studio" @click=${handleOpenWebsite}>our website.</a>
2525
</p>
2626
<!--
2727
<p>

client/components/src/Client.js

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@
1515
* limitations under the License.
1616
*/
1717

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

21-
async function GET(path){
22-
return await fetch(`${ROUTE}/${path}`)
21+
async function GET(path) {
22+
return await fetch(`/${path}`)
2323
}
2424

25-
async function POST(path, body){
26-
return await fetch(`${ROUTE}/${path}`, {
27-
method : 'POST',
28-
body : JSON.stringify(body),
29-
headers : {
30-
Accept : 'application/json',
31-
'Content-Type' : 'application/json'
25+
async function POST(path, body) {
26+
return await fetch(`/${path}`, {
27+
method: 'POST',
28+
body: JSON.stringify(body),
29+
headers: {
30+
Accept: 'application/json',
31+
'Content-Type': 'application/json'
3232
}
3333
})
3434
}
3535

36-
async function CALL(method, args){
37-
const response = await fetch(`${ROUTE}/call`, {
38-
method : 'POST',
39-
body : JSON.stringify({ method, args }),
40-
headers : {
41-
Accept : 'application/json',
42-
'Content-Type' : 'application/json'
36+
async function CALL(method, args) {
37+
const response = await fetch(`/call`, {
38+
method: 'POST',
39+
body: JSON.stringify({ method, args }),
40+
headers: {
41+
Accept: 'application/json',
42+
'Content-Type': 'application/json'
4343
}
4444
})
4545
return await response.json()
@@ -48,22 +48,22 @@ async function CALL(method, args){
4848
/**
4949
* Returns true if it's connected to Live
5050
*/
51-
export async function connected(){
51+
export async function connected() {
5252
try {
5353
await GET('status')
5454
return true
55-
} catch (e){
55+
} catch (e) {
5656
return false
5757
}
5858
}
5959

60-
export async function setNotes(args){
60+
export async function setNotes(args) {
6161
args.notes = encode(args.notes)
6262
const response = await CALL('set_notes', args)
6363
return response
6464
}
6565

66-
export async function getNotes(id){
66+
export async function getNotes(id) {
6767
const response = await CALL('get_notes', { id })
6868
response.notes = decode(response.notes)
6969
return response
@@ -72,20 +72,20 @@ export async function getNotes(id){
7272
/**
7373
* Get all the tracks
7474
*/
75-
export async function tracks(){
75+
export async function tracks() {
7676
return await CALL('tracks')
7777
}
7878

7979
/**
8080
* Get all the clips in a track
8181
*/
82-
export async function clips(id){
82+
export async function clips(id) {
8383
return await CALL('clips', { id })
8484
}
8585

8686
/**
8787
* Make sure a track has at least this many clips
8888
*/
89-
export async function setTrackLength(id, length){
89+
export async function setTrackLength(id, length) {
9090
return await CALL('set_track_length', { length, id })
9191
}

client/interpolate/Model.js

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,51 +19,50 @@ import { MusicVAE, sequences } from '@magenta/music'
1919
const { quantizeNoteSequence, unquantizeSequence, clone } = sequences
2020

2121
export class Model {
22-
constructor(drums=false){
22+
constructor(drums = false) {
2323
// const melodyUrl = 'https://storage.googleapis.com/magentadata/js/checkpoints/music_vae/mel_4bar_med_q2'
2424
// const drumsUrl = 'https://storage.googleapis.com/magentadata/js/checkpoints/music_vae/drums_4bar_med_q2'
2525
const drumUrl = '/interpolate/models/drums_4bar_med'
2626
const melodyUrl = '/interpolate/models/mel_4bar_med'
2727
this.model = new MusicVAE(drums ? drumUrl : melodyUrl)
2828
}
2929

30-
async load(){
30+
async load() {
3131
try {
3232
await this.model.initialize()
33-
} catch (e){
33+
} catch (e) {
3434
const snackbar = document.createElement('magenta-snackbar')
3535
document.body.appendChild(snackbar)
3636
}
3737
}
3838

39-
validateSequence(seqA, seqB){
39+
validateSequence(seqA, seqB) {
4040
const maxBeats = 4 * 16
4141
//make sure they're the same number of measures
4242
const len = Math.min(seqA.totalQuantizedSteps, seqB.totalQuantizedSteps, maxBeats)
4343
this.trim(seqA, len)
4444
this.trim(seqB, len)
4545
}
4646

47-
trim(sequence, beats){
47+
trim(sequence, beats) {
4848
sequence.totalQuantizedSteps = beats
4949
sequence.totalTime = beats * 0.25
5050
sequence.notes = sequence.notes.filter(n => n.quantizedEndStep < beats)
51-
console.log(sequence)
5251
}
5352

54-
trimOutput(inSequence, outSequences){
53+
trimOutput(inSequence, outSequences) {
5554
outSequences.forEach(seq => {
5655
this.trim(seq, inSequence.totalQuantizedSteps)
5756
})
5857
}
5958

60-
setVelocities(sequences){
59+
setVelocities(sequences) {
6160
sequences.forEach(seq => {
6261
seq.notes.forEach(n => n.velocity = 100)
6362
})
6463
}
6564

66-
async predict(inputSequenceA, inputSequenceB, steps=4, temp=1){
65+
async predict(inputSequenceA, inputSequenceB, steps = 4, temp = 1) {
6766
const quarterNoteSubdiv = 4
6867
inputSequenceA = quantizeNoteSequence(inputSequenceA, quarterNoteSubdiv)
6968
inputSequenceB = quantizeNoteSequence(inputSequenceB, quarterNoteSubdiv)
@@ -75,22 +74,22 @@ export class Model {
7574
return outSequences
7675
}
7776

78-
concat(...args){
79-
if (args.length === 2){
77+
concat(...args) {
78+
if (args.length === 2) {
8079
const [seqA, seqB] = args
8180
const outputSequence = clone(seqA)
8281
seqB.notes.forEach(note => {
8382
const clonedNote = Object.assign({}, note)
84-
clonedNote.startTime += outputSequence.totalQuantizedSteps/4
85-
clonedNote.endTime += outputSequence.totalQuantizedSteps/4
83+
clonedNote.startTime += outputSequence.totalQuantizedSteps / 4
84+
clonedNote.endTime += outputSequence.totalQuantizedSteps / 4
8685
clonedNote.quantizedStartStep += seqA.totalQuantizedSteps
8786
clonedNote.quantizedEndStep += seqA.totalQuantizedSteps
8887
outputSequence.notes.push(clonedNote)
8988
})
9089
outputSequence.totalQuantizedSteps = seqA.totalQuantizedSteps + seqB.totalQuantizedSteps
9190
outputSequence.totalTime = outputSequence.totalQuantizedSteps / 4
9291
return outputSequence
93-
} else if (args.length > 2){
92+
} else if (args.length > 2) {
9493
//concat the last two
9594
//get the first arg
9695
const first = args.shift()
@@ -100,10 +99,10 @@ export class Model {
10099
}
101100
}
102101

103-
duplicate(seqs, repeats){
102+
duplicate(seqs, repeats) {
104103
const outSeq = []
105104
seqs.forEach(s => {
106-
for (let i = 0; i < repeats; i++){
105+
for (let i = 0; i < repeats; i++) {
107106
outSeq.push(s)
108107
}
109108
})

magenta4live.amxd/code/magenta-index.js

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,24 @@ const max = require('max-api')
2727
// CONFIG
2828
///////////////////////////////////////////////////////////////////////////////
2929

30-
const PORT = 3333
30+
let PORT = 3333
3131
app.use(express.static('./public'))
32-
app.get('/continue', function(req, res){
33-
res.sendFile('./public/index.html', { root : __dirname })
32+
app.get('/continue', function (req, res) {
33+
res.sendFile('./public/index.html', { root: __dirname })
3434
})
35-
app.get('/drumify', function(req, res){
36-
res.sendFile('./public/index.html', { root : __dirname })
35+
app.get('/drumify', function (req, res) {
36+
res.sendFile('./public/index.html', { root: __dirname })
3737
})
38-
app.get('/generate', function(req, res){
39-
res.sendFile('./public/index.html', { root : __dirname })
38+
app.get('/generate', function (req, res) {
39+
res.sendFile('./public/index.html', { root: __dirname })
4040
})
41-
app.get('/groove', function(req, res){
42-
res.sendFile('./public/index.html', { root : __dirname })
41+
app.get('/groove', function (req, res) {
42+
res.sendFile('./public/index.html', { root: __dirname })
4343
})
44-
app.get('/interpolate', function(req, res){
45-
res.sendFile('./public/index.html', { root : __dirname })
44+
app.get('/interpolate', function (req, res) {
45+
res.sendFile('./public/index.html', { root: __dirname })
4646
})
47-
app.get('/studio', function(req, res){
47+
app.get('/studio', function (req, res) {
4848
max.outlet('openWebsite')
4949
res.send('success')
5050
})
@@ -53,7 +53,7 @@ app.get('/studio', function(req, res){
5353
// ROUTES
5454
///////////////////////////////////////////////////////////////////////////////
5555

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

7979
let server = null
8080

81-
async function startServer(){
81+
async function startServer() {
8282
const availPort = await detect(PORT)
83-
if (PORT !== availPort){
83+
if (PORT !== availPort) {
8484
//try and kill the port, and then try again
85-
console.log(`killing server on ${PORT}`)
86-
await kill(PORT)
85+
// console.log(`killing server on ${PORT}`)
86+
// await kill(PORT)
8787
//try again
88+
PORT = PORT + 1
8889
max.outlet('server', 0)
89-
setTimeout(() => startServer(), 1000)
90+
// setTimeout(() => startServer(), 1000)
91+
setTimeout(() => startServer(), 1)
9092
} else {
9193
server = app.listen(PORT, () => {
92-
max.outlet('server', 1)
94+
max.outlet('server', 1, PORT)
9395
console.log(`server started on ${PORT}`)
9496
})
9597
}
9698
}
9799

98100
startServer()
99101

102+
max.addHandler("closing", () => {
103+
if (server) {
104+
server.close()
105+
}
106+
});
107+
100108
process.on('exit', () => {
101-
if (server){
109+
if (server) {
102110
server.close()
103111
}
104112
})

magenta4live.amxd/code/magenta-js.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ function out(responseId, data){
3030
*/
3131
function remote_invoke(method, args, responseId){
3232
try {
33+
post("received recponseId " + responseId + "\n");
3334
args = JSON.parse(args)
3435
var response = this[method](args)
3536
out(responseId, response)

0 commit comments

Comments
 (0)