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); } }