Last active
November 3, 2017 15:14
-
-
Save xavriley/3c319f82d896762ee75647dfe91cdc00 to your computer and use it in GitHub Desktop.
Sonic Pi timing demo in Opal Ruby
This file contains 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 characters
// Proof of concept for blocking sleep with iteration using jsfiddle | |
// note the parallel iteration | |
function sleep(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
async function demo() { | |
console.log("boom") | |
await sleep(1000); | |
} | |
async function demo2() { | |
while(true) { | |
await demo(); | |
} | |
} | |
async function demo3() { | |
console.log("tick") | |
await sleep(500); | |
} | |
async function demo4() { | |
while(true) { | |
await demo3(); | |
} | |
} | |
demo2(); | |
demo4(); |
This file contains 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 characters
# takes 20 seconds to run so be patient | |
start_time = Time.now.to_f | |
sched_ahead = 0.5 | |
virtual_time = start_time + sched_ahead | |
10.times do | |
sleep_time = 2 | |
# calculate an "ideal" time that we'd like to sleep for | |
# and cache it | |
virtual_time = virtual_time + sleep_time | |
# get the real time | |
# This will most likely be a few milliseconds further on | |
# than our ideal time because of computation time etc. | |
now = Time.now.to_f | |
puts now | |
# sleep for slightly less than our ideal time, | |
# taking into account the delay | |
sleep(virtual_time - now) | |
end |
This file contains 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 characters
window.AudioContext = window.AudioContext || window.webkitAudioContext; | |
/// custom buffer loader | |
/// see http://www.html5rocks.com/en/tutorials/webaudio/intro/ | |
function BufferLoader(context, urlList, callback) { | |
this.context = context; | |
this.urlList = urlList; | |
this.onload = callback; | |
this.bufferList = new Array(); | |
this.loadCount = 0; | |
} | |
BufferLoader.prototype.loadBuffer = function (url, index) { | |
// Load buffer asynchronously | |
var request = new XMLHttpRequest(); | |
request.open("GET", url, true); | |
request.responseType = "arraybuffer"; | |
var loader = this; | |
request.onload = function () { | |
// Asynchronously decode the audio file data in request.response | |
loader.context.decodeAudioData( | |
request.response, | |
function (buffer) { | |
if (!buffer) { | |
alert('error decoding file data: ' + url); | |
return; | |
} | |
loader.bufferList[index] = buffer; | |
if (++loader.loadCount == loader.urlList.length) loader.onload(loader.bufferList); | |
}, | |
function (error) { | |
console.error('decodeAudioData error', error); | |
}); | |
} | |
request.onerror = function (e) { | |
alert('BufferLoader: XHR error'); | |
console.log(e); | |
} | |
request.send(); | |
} | |
BufferLoader.prototype.load = function () { | |
for (var i = 0; i < this.urlList.length; ++i) | |
this.loadBuffer(this.urlList[i], i); | |
} | |
/// setup audio context and start loading samples | |
var actx = | |
new AudioContext(), | |
blst, | |
bLoader = new BufferLoader( | |
actx, [ | |
'https://dl.dropboxusercontent.com/s/mide1jl8ks3hdmd/drum_cymbal_closed.flac', | |
'https://dl.dropboxusercontent.com/s/mide1jl8ks3hdmd/drum_cymbal_closed.flac', | |
'https://dl.dropboxusercontent.com/s/zc5nu4pm4oree63/bd_haus.flac'], | |
done), | |
isReady = false; | |
/// start loading the samples | |
bLoader.load(); | |
/// when samples are loaded update status | |
function done(bl) { | |
blst = bl; | |
isReady = true; | |
$('#status').html('Ready!'); | |
} | |
/// this sets up chain so we can play audio | |
function play(i) { | |
var src = actx.createBufferSource(); | |
src.buffer = blst[i]; | |
src.connect(actx.destination); | |
src.start(0); | |
} | |
/// check keys | |
$(window).bind("keydown", function (key) { | |
if (!isReady) return; | |
switch (parseInt(key.which, 10)) { | |
case 65: | |
play(0); | |
break; | |
case 83: | |
play(1); | |
break; | |
case 68: | |
play(2); | |
break; | |
} | |
}) |
Another sexp example - maybe adding a rewriter for blocks only would be simpler?
require 'js'
class Foo
include JS
def self.bar(&block)
JS.async block.call
end
end
Foo.bar do
JS.await sleep(2)
end
$ bundle exec opal --sexp test.js.rb
s(:begin,
s(:send, nil, :require,
s(:str, "js")),
s(:class,
s(:const, nil, :Foo), nil,
s(:begin,
s(:send, nil, :include,
s(:const, nil, :JS)),
s(:defs,
s(:self), :bar,
s(:args,
s(:blockarg, :block)),
s(:send,
s(:const, nil, :JS), :async,
s(:send,
s(:lvar, :block), :call))))),
s(:send,
s(:const, nil, :Foo), :bar,
s(:iter,
s(:args),
s(:send,
s(:const, nil, :JS), :await,
s(:send, nil, :sleep,
s(:int, 2))))))
I'm using this gist as a sort of work log as I try things out. I did have second thoughts around contributing to Opal based on some history around that particular project. I was going to include the following statement in my commit message but after engaging with the core committers there I agreed to use a different forum. I'm including the comment here for posterity:
Finally, if my contributions to the project are accepted I agree to the
terms of the code of conduct. For my own peace of mind though, I would
like to go on record to say that I don't share the views of other
maintainers on the issue of transgender discrimination in particular.
I don't want to make any more political statements than necessary as
this project has already hosted a great deal of discussion. However, as a sibling of
mine is a) currently transitioning gender and b) working towards a career in coding I
don't feel I can ignore the issue with a clear conscience.
Rather than walking away or ignoring the project I have chosen to engage and to
contribute if I can. My view is that I'd like to move forward with
positivity but this doesn't imply that I condone or support the views
I've seen expressed by maintainers elsewhere.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
suggested approach from Gitter - use the
JS
constant to mark functionsThis would create the following sexp
In this case we could use a Rewriter to define a transform for
s(:const, nil, :JS), :async,
tos(:async, ...)
(or something). We can then write a new node type inlib/opal/nodes/async.rb
to push theasync
keyword (I think)