Skip to content

Commit

Permalink
audio input (sharpie7#342)
Browse files Browse the repository at this point in the history
  • Loading branch information
pfalstad committed Jun 17, 2019
1 parent 84ec986 commit 10bac5b
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 5 deletions.
222 changes: 222 additions & 0 deletions src/com/lushprojects/circuitjs1/client/AudioInputElm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/*
Copyright (C) Paul Falstad and Iain Sharp
This file is part of CircuitJS1.
CircuitJS1 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
CircuitJS1 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CircuitJS1. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lushprojects.circuitjs1.client;

import com.google.gwt.core.client.JsArrayNumber;
import java.util.HashMap;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.FileUpload;

class AudioFileEntry {
String fileName;
JsArrayNumber data;
}

class AudioInputElm extends RailElm {
JsArrayNumber data;
double timeOffset;
int samplingRate;
int fileNum;
String fileName;
double maxVoltage;
double startPosition;

static int lastSamplingRate;

static int fileNumCounter = 1;
static HashMap<Integer, AudioFileEntry> audioFileMap = new HashMap<Integer, AudioFileEntry>();

public AudioInputElm(int xx, int yy) {
super(xx, yy, WF_AC);
maxVoltage = 5;
}

public AudioInputElm(int xa, int ya, int xb, int yb, int f,
StringTokenizer st) {
super(xa, ya, xb, yb, f, st);
waveform = WF_AC;
maxVoltage = Double.parseDouble(st.nextToken());
startPosition = Double.parseDouble(st.nextToken());
fileNum = Integer.parseInt(st.nextToken());

AudioFileEntry ent = audioFileMap.get(fileNum);
if (ent != null) {
fileName = ent.fileName;
data = ent.data;
}
samplingRate = lastSamplingRate;
}

double fmphase;

String dump() {
// add a file number to the dump so we can preserve the audio file data when doing cut and paste, or undo/redo.
// we don't save the entire file in the dump because it would be huge.
if (data != null) {
if (fileNum == 0)
fileNum = fileNumCounter++;
AudioFileEntry ent = new AudioFileEntry();
ent.fileName = fileName;
ent.data = data;
audioFileMap.put(fileNum, ent);
}
return super.dump() + " " + maxVoltage + " " + startPosition + " " + fileNum;
}

void reset() {
timeOffset = startPosition;
}

void drawRail(Graphics g) {
drawRailText(g, fileName == null ? "No file" : fileName);
}

String getRailText() {
return fileName == null ? "No file" : fileName;
}

void setSampleRate(int sr) {
samplingRate = sr;
}

double getVoltage() {
if (data == null)
return 0;
if (timeOffset < startPosition)
timeOffset = startPosition;
int ptr = (int) (timeOffset * samplingRate);
if (ptr >= data.length()) {
ptr = 0;
timeOffset = 0;
}
return data.get(ptr) * maxVoltage;
}

void stepFinished() {
timeOffset += sim.timeStep;
}

int getDumpType() { return 411; }
int getShortcut() { return 0; }

public EditInfo getEditInfo(int n) {
if (n == 0) {
EditInfo ei = new EditInfo("", 0, -1, -1);
final AudioInputElm thisElm = this;
final FileUpload file = new FileUpload();
ei.widget = file;
file.addChangeHandler(new ChangeHandler() {
public void onChange(ChangeEvent event) {
fileName = file.getFilename().replaceAll("^.*\\\\", "").replaceAll("\\.[^.]*$", "");
// AudioInputElm.fetchAudio(thisElm, file.getFilename());
AudioInputElm.fetchLoadFileData(thisElm, file.getElement());
}
});
return ei;
}
if (n == 1)
return new EditInfo("Max Voltage", maxVoltage);
if (n == 2)
return new EditInfo("Start Position (s)", startPosition);
return null;
}

public void setEditValue(int n, EditInfo ei) {
if (n == 1)
maxVoltage = ei.value;
if (n == 2)
startPosition = ei.value;
}

static native boolean oldfetchAudio(AudioInputElm elm, String file) /*-{
var context = new (window.AudioContext || window.webkitAudioContext)();
var url = file;
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
var audioData = request.response;
context.decodeAudioData(audioData, function(buffer) {
var data = buffer.getChannelData(0);
[email protected]::gotAudioData(*)(data);
},
function(e){ console.log("Error with decoding audio data" + e.err); });
}
request.send();
}-*/;


static native String fetchLoadFileData(AudioInputElm elm, Element element) /*-{
var oFiles = element.files;
var context = new (window.AudioContext || window.webkitAudioContext)();
[email protected]::setSampleRate(I)(context.sampleRate);
if (oFiles.length >= 1) {
var reader = new FileReader();
reader.onload = function(e) {
context.decodeAudioData(reader.result, function(buffer) {
var data = buffer.getChannelData(0);
[email protected]::gotAudioData(*)(data);
},
function(e){ console.log("Error with decoding audio data" + e.err); });
};
reader.readAsArrayBuffer(oFiles[0]);
}
}-*/;

static native boolean fetchAudio(AudioInputElm elm, String audioData) /*-{
var context = new (window.AudioContext || window.webkitAudioContext)();
var url = file;
context.decodeAudioData(audioData, function(buffer) {
var data = buffer.getChannelData(0);
[email protected]::gotAudioData(*)(data);
},
function(e){ console.log("Error with decoding audio data" + e.err); });
}-*/;

void gotAudioData(JsArrayNumber d) {
data = d;
lastSamplingRate = samplingRate;
AudioOutputElm.lastSamplingRate = samplingRate;
}

void getInfo(String arr[]) {
arr[0] = "audio input";
if (data == null) {
arr[1] = "no file loaded";
return;
}
arr[1] = "V = " + getVoltageText(volts[0]);
arr[2] = "pos = " + getUnitText(timeOffset, "s");
double dur = data.length() / (double)samplingRate;
arr[3] = "dur = " + getUnitText(dur, "s");
}

public static void clearCache() {
audioFileMap.clear();
}
}
7 changes: 7 additions & 0 deletions src/com/lushprojects/circuitjs1/client/CirSim.java
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ public void composeMainMenu(MenuBar mainMenuBar) {
inputMenuBar.addItem(getClassCheckItem(LS("Add FM Source"), "FMElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Current Source"), "CurrentElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Noise Generator"), "NoiseElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Audio Input"), "AudioInputElm"));

mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS("&nbsp;</div>Inputs and Sources")), inputMenuBar);

Expand Down Expand Up @@ -3226,6 +3227,8 @@ void readSetup(byte b[], boolean retain, boolean centre) {
needAnalyze();
if (centre)
centreCircuit();

AudioInputElm.clearCache(); // to save memory
}

// delete sliders for an element
Expand Down Expand Up @@ -4847,6 +4850,8 @@ public static CircuitElm createCe(int tint, int x1, int y1, int x2, int y2, int
return new OpAmpRealElm(x1, y1, x2, y2, f, st);
if (tint==410)
return new CustomCompositeElm(x1, y1, x2, y2, f, st);
if (tint==411)
return new AudioInputElm(x1, y1, x2, y2, f, st);
return null;
}

Expand Down Expand Up @@ -5074,6 +5079,8 @@ public static CircuitElm constructElement(String n, int x1, int y1){
return (CircuitElm) new OpAmpRealElm(x1, y1);
if (n=="CustomCompositeElm")
return (CircuitElm) new CustomCompositeElm(x1, y1);
if (n=="AudioInputElm")
return (CircuitElm) new AudioInputElm(x1, y1);
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public EditInfo getEditInfo(int n) {
String fname = "data-"+ dtf.format(date) + ".circuitjs.txt";
Anchor a=new Anchor(fname, url);
a.getElement().setAttribute("Download", fname);
ei.anchor = a;
ei.widget = a;
return ei;
}
return null;
Expand Down
4 changes: 2 additions & 2 deletions src/com/lushprojects/circuitjs1/client/EditDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ public void onClick(ClickEvent event) {
});
} else if (ei.textArea != null) {
vp.insert(ei.textArea, idx);
} else if (ei.anchor != null) {
vp.insert(ei.anchor, idx);
} else if (ei.widget != null) {
vp.insert(ei.widget, idx);
} else {
vp.insert(ei.textf = new TextBox(), idx);
if (ei.text != null)
Expand Down
5 changes: 3 additions & 2 deletions src/com/lushprojects/circuitjs1/client/EditInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package com.lushprojects.circuitjs1.client;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.TextArea;
Expand Down Expand Up @@ -52,15 +53,15 @@ int changeFlag(int flags, int bit) {
Checkbox checkbox;
Button button;
TextArea textArea;
Anchor anchor;
Widget widget;
boolean newDialog;
boolean dimensionless;

// for slider dialog
TextBox minBox, maxBox, labelBox;

boolean canCreateAdjustable() {
return choice == null && checkbox == null && button == null && textArea == null && anchor == null;
return choice == null && checkbox == null && button == null && textArea == null && widget == null;
}
}

9 changes: 9 additions & 0 deletions src/com/lushprojects/circuitjs1/client/RailElm.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,16 @@ void setPoints() {
lead1 = interpPoint(point1, point2, 1-circleSize/dn);
}

String getRailText() {
return null;
}

void draw(Graphics g) {
String rt = getRailText();
double w = rt == null ? circleSize : g.context.measureText(rt).getWidth()/2;
if (w > dn*.8)
w = dn*.8;
lead1 = interpPoint(point1, point2, 1-w/dn);
setBbox(point1, point2, circleSize);
setVoltageColor(g, volts[0]);
drawThickLine(g, point1, lead1);
Expand Down

0 comments on commit 10bac5b

Please sign in to comment.