diff --git a/pom.xml b/pom.xml
index faab9c44..8ea0ddb1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
sh.ball
osci-render
- 1.11.4
+ 1.11.5
osci-render
diff --git a/src/main/java/sh/ball/audio/AudioPlayer.java b/src/main/java/sh/ball/audio/AudioPlayer.java
index 7ae4f8a9..c72bba79 100644
--- a/src/main/java/sh/ball/audio/AudioPlayer.java
+++ b/src/main/java/sh/ball/audio/AudioPlayer.java
@@ -14,10 +14,14 @@ public interface AudioPlayer extends Runnable {
boolean isPlaying();
- void setFrequency(double frequency);
+ void setBaseFrequency(double frequency);
+
+ void setPitchBendFactor(double pitchBend);
double getFrequency();
+ double getBaseFrequency();
+
void addFrame(S frame);
void addEffect(Object identifier, Effect effect);
diff --git a/src/main/java/sh/ball/audio/ShapeAudioPlayer.java b/src/main/java/sh/ball/audio/ShapeAudioPlayer.java
index 58333a02..3e425da9 100644
--- a/src/main/java/sh/ball/audio/ShapeAudioPlayer.java
+++ b/src/main/java/sh/ball/audio/ShapeAudioPlayer.java
@@ -44,6 +44,7 @@ public class ShapeAudioPlayer implements AudioPlayer> {
private double lengthDrawn = 0;
private int count = 0;
private double frequency = MIDDLE_C;
+ private double pitchBend = 1.0;
private double trace = 0.5;
private AudioDevice device;
@@ -133,16 +134,27 @@ public class ShapeAudioPlayer implements AudioPlayer> {
}
@Override
- public void setFrequency(double frequency) {
+ public void setBaseFrequency(double frequency) {
this.frequency = frequency;
updateLengthIncrement();
}
@Override
- public double getFrequency() {
+ public void setPitchBendFactor(double pitchBend) {
+ this.pitchBend = pitchBend;
+ updateLengthIncrement();
+ }
+
+ @Override
+ public double getBaseFrequency() {
return frequency;
}
+ @Override
+ public double getFrequency() {
+ return frequency * pitchBend;
+ }
+
private Shape getCurrentShape() {
if (frame.size() == 0) {
return new Vector2();
@@ -154,7 +166,8 @@ public class ShapeAudioPlayer implements AudioPlayer> {
private void updateLengthIncrement() {
double totalLength = Shape.totalLength(frame);
int sampleRate = device.sampleRate();
- lengthIncrement = Math.max(totalLength / (sampleRate / frequency), MIN_LENGTH_INCREMENT);
+ double actualFrequency = frequency * pitchBend;
+ lengthIncrement = Math.max(totalLength / (sampleRate / actualFrequency), MIN_LENGTH_INCREMENT);
}
@Override
diff --git a/src/main/java/sh/ball/gui/Controller.java b/src/main/java/sh/ball/gui/Controller.java
index 56a7949e..985c6d79 100644
--- a/src/main/java/sh/ball/gui/Controller.java
+++ b/src/main/java/sh/ball/gui/Controller.java
@@ -54,6 +54,9 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
private static final InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj");
private static final double MAX_FREQUENCY = 12000;
+ private static final int PITCH_BEND_DATA_LENGTH = 7;
+ private static final int PITCH_BEND_MAX = 16383;
+ private static final int PITCH_BEND_SEMITONES = 2;
private final FileChooser fileChooser = new FileChooser();
private final DirectoryChooser folderChooser = new DirectoryChooser();
@@ -213,7 +216,7 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
private Map> initializeSliderMap() {
return Map.of(
- frequencySlider, f -> audioPlayer.setFrequency(Math.pow(MAX_FREQUENCY, f)),
+ frequencySlider, f -> audioPlayer.setBaseFrequency(Math.pow(MAX_FREQUENCY, f)),
rotateSpeedSlider, rotateEffect::setSpeed,
translationSpeedSlider, translateEffect::setSpeed,
scaleSlider, scaleEffect::setScale,
@@ -631,7 +634,7 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
private void playNote(double frequency, double volume) {
frequencySlider.setValue(Math.log(frequency) / Math.log(MAX_FREQUENCY));
- audioPlayer.setFrequency(frequency);
+ audioPlayer.setBaseFrequency(frequency);
scaleSlider.setValue(volume);
}
@@ -688,6 +691,19 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
}
playNote(frequency, volume);
}
+ } else if (command == ShortMessage.PITCH_BEND) {
+ // using these instructions https://sites.uci.edu/camp2014/2014/04/30/managing-midi-pitchbend-messages/
+
+ int pitchBend = (message.getData2() << PITCH_BEND_DATA_LENGTH) | message.getData1();
+ // get pitch bend in range -1 to 1
+ double pitchBendFactor = (double) pitchBend / PITCH_BEND_MAX;
+ pitchBendFactor = 2 * pitchBendFactor - 1;
+ pitchBendFactor *= PITCH_BEND_SEMITONES;
+ // 12 tone equal temperament
+ pitchBendFactor /= 12;
+ pitchBendFactor = Math.pow(2, pitchBendFactor);
+
+ audioPlayer.setPitchBendFactor(pitchBendFactor);
}
}