kopia lustrzana https://github.com/jameshball/osci-render
Add audio sample drop-down in Audio settings for recording to wav
rodzic
538db5f137
commit
0a661da836
|
@ -14,6 +14,7 @@ import sh.ball.audio.effect.SineEffect;
|
|||
import sh.ball.audio.effect.SmoothEffect;
|
||||
import sh.ball.audio.engine.AudioDevice;
|
||||
import sh.ball.audio.engine.AudioEngine;
|
||||
import sh.ball.audio.engine.AudioSample;
|
||||
import sh.ball.audio.midi.MidiCommunicator;
|
||||
import sh.ball.audio.midi.MidiNote;
|
||||
import sh.ball.shapes.Shape;
|
||||
|
@ -85,7 +86,8 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
|
|||
private int sampleRate;
|
||||
private boolean flipX = false;
|
||||
private boolean flipY = false;
|
||||
private boolean recordHQ = true;
|
||||
private double brightness = 1.0;
|
||||
private AudioSample audioSample = AudioSample.INT16;
|
||||
|
||||
private AudioDevice device;
|
||||
|
||||
|
@ -99,9 +101,8 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
|
|||
communicator.addListener(this);
|
||||
}
|
||||
|
||||
public boolean toggleHQRecording() {
|
||||
recordHQ = !recordHQ;
|
||||
return recordHQ;
|
||||
public void setAudioSample(AudioSample sample) {
|
||||
this.audioSample = sample;
|
||||
}
|
||||
public boolean midiPlaying() {
|
||||
return numKeysDown.get() > 0;
|
||||
|
@ -213,6 +214,20 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
|
|||
}
|
||||
|
||||
private void writeChannels(float leftChannel, float rightChannel) {
|
||||
double[] channels = new double[device.channels()];
|
||||
Arrays.fill(channels, brightness);
|
||||
|
||||
if (channels.length > 0) {
|
||||
channels[0] = leftChannel;
|
||||
}
|
||||
if (channels.length > 1) {
|
||||
channels[1] = rightChannel;
|
||||
}
|
||||
|
||||
if (recording) {
|
||||
AudioSample.writeAudioSample(audioSample, channels, outputStream::write);
|
||||
}
|
||||
|
||||
int left = (int) (leftChannel * Short.MAX_VALUE);
|
||||
int right = (int) (rightChannel * Short.MAX_VALUE);
|
||||
|
||||
|
@ -221,32 +236,6 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
|
|||
byte b2 = (byte) right;
|
||||
byte b3 = (byte) (right >> 8);
|
||||
|
||||
if (recording) {
|
||||
if (recordHQ) {
|
||||
int leftR = (int) (leftChannel * 8388606);
|
||||
int rightR = (int) (rightChannel * 8388606);
|
||||
|
||||
byte b0R = (byte) (leftR);
|
||||
byte b1R = (byte) (leftR >> 8);
|
||||
byte b2R = (byte) (leftR >> 16);
|
||||
byte b3R = (byte) (rightR);
|
||||
byte b4R = (byte) (rightR >> 8);
|
||||
byte b5R = (byte) (rightR >> 16);
|
||||
|
||||
outputStream.write(b0R);
|
||||
outputStream.write(b1R);
|
||||
outputStream.write(b2R);
|
||||
outputStream.write(b3R);
|
||||
outputStream.write(b4R);
|
||||
outputStream.write(b5R);
|
||||
} else {
|
||||
outputStream.write(b0);
|
||||
outputStream.write(b1);
|
||||
outputStream.write(b2);
|
||||
outputStream.write(b3);
|
||||
}
|
||||
}
|
||||
|
||||
for (Listener listener : listeners) {
|
||||
listener.write(b0);
|
||||
listener.write(b1);
|
||||
|
@ -517,13 +506,14 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
|
|||
byte[] input = outputStream.toByteArray();
|
||||
outputStream = null;
|
||||
|
||||
AudioFormat audioFormat = new AudioFormat(sampleRate, (recordHQ ? BITS_PER_SAMPLE_HQ : BITS_PER_SAMPLE), NUM_OUTPUTS, SIGNED, BIG_ENDIAN);
|
||||
AudioFormat audioFormat = new AudioFormat(sampleRate, audioSample.size, device.channels(), audioSample.signed, BIG_ENDIAN);
|
||||
|
||||
return new AudioInputStream(new ByteArrayInputStream(input), audioFormat, framesRecorded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBrightness(double brightness) {
|
||||
this.brightness = brightness;
|
||||
audioEngine.setBrightness(brightness);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,84 @@
|
|||
package sh.ball.audio.engine;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// defines the different kinds of support audio samples for different audio
|
||||
// devices
|
||||
public enum AudioSample {
|
||||
UINT8,
|
||||
INT8,
|
||||
INT16,
|
||||
INT24,
|
||||
INT32,
|
||||
FLOAT32,
|
||||
FLOAT64
|
||||
UINT8(8, false),
|
||||
INT8(8, true),
|
||||
INT16(16, true),
|
||||
INT24(24, true),
|
||||
INT32(32, true),
|
||||
FLOAT32(32, true),
|
||||
FLOAT64(64, true);
|
||||
|
||||
public final int size;
|
||||
public final boolean signed;
|
||||
|
||||
AudioSample(int size, boolean signed) {
|
||||
this.size = size;
|
||||
this.signed = signed;
|
||||
}
|
||||
|
||||
public static void writeAudioSample(AudioSample sample, double[] channels, Consumer<Byte> writeByte) {
|
||||
switch (sample) {
|
||||
case UINT8 -> {
|
||||
for (double channel : channels) {
|
||||
writeByte.accept((byte) ((int) (127 * (channel + 1))));
|
||||
}
|
||||
}
|
||||
case INT8 -> {
|
||||
for (double channel : channels) {
|
||||
writeByte.accept((byte) ((int) (127 * channel)));
|
||||
}
|
||||
}
|
||||
case INT16 -> {
|
||||
for (double channel : channels) {
|
||||
short shortChannel = (short) (channel * Short.MAX_VALUE);
|
||||
writeByte.accept((byte) shortChannel);
|
||||
writeByte.accept((byte) (shortChannel >> 8));
|
||||
}
|
||||
}
|
||||
case INT24 -> {
|
||||
for (double channel : channels) {
|
||||
int intChannel = (int) (channel * 8388606);
|
||||
writeByte.accept((byte) intChannel);
|
||||
writeByte.accept((byte) (intChannel >> 8));
|
||||
writeByte.accept((byte) (intChannel >> 16));
|
||||
}
|
||||
}
|
||||
case INT32 -> {
|
||||
for (double channel : channels) {
|
||||
int intChannel = (int) (channel * Integer.MAX_VALUE);
|
||||
writeByte.accept((byte) intChannel);
|
||||
writeByte.accept((byte) (intChannel >> 8));
|
||||
writeByte.accept((byte) (intChannel >> 16));
|
||||
writeByte.accept((byte) (intChannel >> 24));
|
||||
}
|
||||
}
|
||||
case FLOAT32 -> {
|
||||
for (double channel : channels) {
|
||||
int intChannel = Float.floatToIntBits((float) channel);
|
||||
writeByte.accept((byte) intChannel);
|
||||
writeByte.accept((byte) (intChannel >> 8));
|
||||
writeByte.accept((byte) (intChannel >> 16));
|
||||
writeByte.accept((byte) (intChannel >> 24));
|
||||
}
|
||||
}
|
||||
case FLOAT64 -> {
|
||||
for (double channel : channels) {
|
||||
long longChannel = Double.doubleToLongBits(channel);
|
||||
writeByte.accept((byte) longChannel);
|
||||
writeByte.accept((byte) (longChannel >> 8));
|
||||
writeByte.accept((byte) (longChannel >> 16));
|
||||
writeByte.accept((byte) (longChannel >> 24));
|
||||
writeByte.accept((byte) (longChannel >> 32));
|
||||
writeByte.accept((byte) (longChannel >> 40));
|
||||
writeByte.accept((byte) (longChannel >> 48));
|
||||
writeByte.accept((byte) (longChannel >> 56));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,10 +43,7 @@ import javax.xml.transform.TransformerFactory;
|
|||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import sh.ball.audio.engine.AudioDevice;
|
||||
import sh.ball.audio.engine.AudioInput;
|
||||
import sh.ball.audio.engine.AudioInputListener;
|
||||
import sh.ball.audio.engine.JavaAudioInput;
|
||||
import sh.ball.audio.engine.*;
|
||||
import sh.ball.audio.midi.MidiListener;
|
||||
import sh.ball.audio.midi.MidiNote;
|
||||
import sh.ball.engine.ObjectServer;
|
||||
|
@ -179,6 +176,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
|||
private Spinner<Double> recordLengthSpinner;
|
||||
@FXML
|
||||
private MenuItem softwareOscilloscopeMenuItem;
|
||||
@FXML
|
||||
private ComboBox<AudioSample> audioSampleComboBox;
|
||||
|
||||
public MainController() throws Exception {
|
||||
// Clone DEFAULT_OBJ InputStream using a ByteArrayOutputStream
|
||||
|
@ -240,6 +239,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
|||
// the recording.
|
||||
private void toggleRecord() {
|
||||
recording = !recording;
|
||||
audioSampleComboBox.setDisable(recording);
|
||||
deviceComboBox.setDisable(recording);
|
||||
boolean timedRecord = recordCheckBox.isSelected();
|
||||
if (recording) {
|
||||
// if it is a timed recording then a timeline is scheduled to start and
|
||||
|
@ -486,6 +487,14 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
|||
}
|
||||
});
|
||||
|
||||
audioSampleComboBox.setItems(FXCollections.observableList(List.of(AudioSample.UINT8, AudioSample.INT8, AudioSample.INT16, AudioSample.INT24, AudioSample.INT32)));
|
||||
audioSampleComboBox.setValue(AudioSample.INT16);
|
||||
audioSampleComboBox.valueProperty().addListener((options, oldSample, sample) -> {
|
||||
if (sample != null) {
|
||||
audioPlayer.setAudioSample(sample);
|
||||
}
|
||||
});
|
||||
|
||||
brightnessSlider.valueProperty().addListener((e, old, brightness) -> audioPlayer.setBrightness(brightness.doubleValue()));
|
||||
|
||||
objController.updateObjectRotateSpeed();
|
||||
|
@ -1146,6 +1155,10 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
|||
midiDecay.appendChild(document.createTextNode(decaySpinner.getValue().toString()));
|
||||
root.appendChild(midiDecay);
|
||||
|
||||
Element audioSample = document.createElement("audioSample");
|
||||
audioSample.appendChild(document.createTextNode(audioSampleComboBox.getValue().name()));
|
||||
root.appendChild(audioSample);
|
||||
|
||||
Element filesElement = document.createElement("files");
|
||||
for (int i = 0; i < openFiles.size(); i++) {
|
||||
Element fileElement = document.createElement("file");
|
||||
|
@ -1269,6 +1282,11 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
|||
decaySpinner.getValueFactory().setValue(Double.parseDouble(midiDecay.getTextContent()));
|
||||
}
|
||||
|
||||
Element audioSample = (Element) root.getElementsByTagName("audioSample").item(0);
|
||||
if (audioSample != null) {
|
||||
audioSampleComboBox.setValue(AudioSample.valueOf(audioSample.getTextContent()));
|
||||
}
|
||||
|
||||
Element filesElement = (Element) root.getElementsByTagName("files").item(0);
|
||||
List<byte[]> files = new ArrayList<>();
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
|
|
|
@ -69,6 +69,16 @@
|
|||
<KeyCodeCombination alt="UP" code="R" control="UP" meta="UP" shift="UP" shortcut="DOWN" />
|
||||
</accelerator>
|
||||
</MenuItem>
|
||||
<CustomMenuItem hideOnClick="false" mnemonicParsing="false" text="Recording Bit Depth">
|
||||
<content>
|
||||
<AnchorPane>
|
||||
<children>
|
||||
<Label prefHeight="25.0" text="Recording Audio Sample" textFill="WHITE" />
|
||||
<ComboBox fx:id="audioSampleComboBox" layoutY="25.0" prefHeight="26.0" prefWidth="376.0" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</CustomMenuItem>
|
||||
<CustomMenuItem hideOnClick="false" mnemonicParsing="false" text="Timed recording?">
|
||||
<content>
|
||||
<CheckBox fx:id="recordCheckBox" text="Timed recording?" />
|
||||
|
|
Ładowanie…
Reference in New Issue