Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for microphone input #54

Closed
wants to merge 9 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 77 additions & 2 deletions src/Builtins.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Random: randstring
import Dates

export Slider, NumberField, Button, CheckBox, TextField, PasswordField, Select, MultiSelect, Radio, FilePicker, DateField, TimeField, ColorStringPicker
export Slider, NumberField, Button, CheckBox, TextField, PasswordField, Select, MultiSelect, Radio, FilePicker, DateField, TimeField, ColorStringPicker, Microphone

struct Slider
range::AbstractRange
Expand Down Expand Up @@ -149,7 +149,6 @@ end
get(textfield::TextField) = textfield.default



"""A password input (`<input type="password">`) - the user can type text, the text is returned as `String` via `@bind`.

This does not provide any special security measures, it just renders black dots (•••) instead of the typed characters.
Expand Down Expand Up @@ -240,6 +239,13 @@ function show(io::IO, ::MIME"text/html", select::MultiSelect)
show(io, MIME"text/html"(), o.second)
else
print(io, o.second)
print(io, """<option value="$(htmlesc(o.first))"$(radio.default === o.first ? " selected" : "")>""")
withtag(io, :option, :value=>o.first) do
if showable(MIME"text/html"(), o.second)
show(io, MIME"text/html"(), o.second)
else
print(io, o.second)
end
end
print(io, "</option>")
end
Expand All @@ -253,6 +259,7 @@ get(select::MultiSelect) = ismissing(select.default) ? Any[] : select.default
The optional `accept` argument can be an array of `MIME`s. The user can only select files with these MIME. If only `image/*` MIMEs are allowed, then smartphone browsers will open the camera instead of a file browser.

## Examples
get(select::Select) = ismissing(select.default) ? first(select.options).first : select.default

`@bind file_data FilePicker()`

Expand Down Expand Up @@ -336,6 +343,74 @@ end

get(radio::Radio) = radio.default

struct Microphone end

function show(io::IO, ::MIME"text/html", microphone::Microphone)
mic_id = randstring('a':'z')
mic_btn_id = randstring('a':'z')
microphone
withtag(() -> (), io, :audio, :id => mic_id)
print(io, """<input type="button" id="$mic_btn_id" class="mic-button" value="Stop">""")
withtag(io, :script) do
print(io, """
const player = document.getElementById('$mic_id');
const stop = document.getElementById('$mic_btn_id');

const handleSuccess = function(stream) {
const context = new AudioContext({ sampleRate: 44100 });
const analyser = context.createAnalyser();
const source = context.createMediaStreamSource(stream);

source.connect(analyser);

const bufferLength = analyser.frequencyBinCount;

let dataArray = new Float32Array(bufferLength);
let animFrame;

const streamAudio = () => {
animFrame = requestAnimationFrame(streamAudio);
analyser.getFloatTimeDomainData(dataArray);
player.value = dataArray;
player.dispatchEvent(new CustomEvent("input"));
}

streamAudio();

stop.onclick = e => {
source.disconnect(analyser);
cancelAnimationFrame(animFrame);
}
}

navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(handleSuccess)
"""
)
end
withtag(io, :style) do
print(io, """
.mic-button {
background-color: darkred;
border: none;
border-radius: 6px;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
font-family: "Alegreya Sans", sans-serif;
margin: 4px 2px;
cursor: pointer;
}
"""
)
end
end

get(microphone::Microphone) = microphone

"""A date input (`<input type="date">`) - the user can pick a date, the date is returned as `Dates.DateTime` via `@bind`.

Use `default` to set the initial value.
Expand Down