Add support for pitch bending with MIDI

pull/35/head
James Ball 2021-07-18 19:24:21 +01:00
rodzic 7ee7f4d644
commit e6b198d0ec
4 zmienionych plików z 40 dodań i 7 usunięć

Wyświetl plik

@ -6,7 +6,7 @@
<groupId>sh.ball</groupId> <groupId>sh.ball</groupId>
<artifactId>osci-render</artifactId> <artifactId>osci-render</artifactId>
<version>1.11.4</version> <version>1.11.5</version>
<name>osci-render</name> <name>osci-render</name>

Wyświetl plik

@ -14,10 +14,14 @@ public interface AudioPlayer<S> extends Runnable {
boolean isPlaying(); boolean isPlaying();
void setFrequency(double frequency); void setBaseFrequency(double frequency);
void setPitchBendFactor(double pitchBend);
double getFrequency(); double getFrequency();
double getBaseFrequency();
void addFrame(S frame); void addFrame(S frame);
void addEffect(Object identifier, Effect effect); void addEffect(Object identifier, Effect effect);

Wyświetl plik

@ -44,6 +44,7 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
private double lengthDrawn = 0; private double lengthDrawn = 0;
private int count = 0; private int count = 0;
private double frequency = MIDDLE_C; private double frequency = MIDDLE_C;
private double pitchBend = 1.0;
private double trace = 0.5; private double trace = 0.5;
private AudioDevice device; private AudioDevice device;
@ -133,16 +134,27 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
} }
@Override @Override
public void setFrequency(double frequency) { public void setBaseFrequency(double frequency) {
this.frequency = frequency; this.frequency = frequency;
updateLengthIncrement(); updateLengthIncrement();
} }
@Override @Override
public double getFrequency() { public void setPitchBendFactor(double pitchBend) {
this.pitchBend = pitchBend;
updateLengthIncrement();
}
@Override
public double getBaseFrequency() {
return frequency; return frequency;
} }
@Override
public double getFrequency() {
return frequency * pitchBend;
}
private Shape getCurrentShape() { private Shape getCurrentShape() {
if (frame.size() == 0) { if (frame.size() == 0) {
return new Vector2(); return new Vector2();
@ -154,7 +166,8 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
private void updateLengthIncrement() { private void updateLengthIncrement() {
double totalLength = Shape.totalLength(frame); double totalLength = Shape.totalLength(frame);
int sampleRate = device.sampleRate(); 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 @Override

Wyświetl plik

@ -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 InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj");
private static final double MAX_FREQUENCY = 12000; 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 FileChooser fileChooser = new FileChooser();
private final DirectoryChooser folderChooser = new DirectoryChooser(); private final DirectoryChooser folderChooser = new DirectoryChooser();
@ -213,7 +216,7 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
private Map<Slider, Consumer<Double>> initializeSliderMap() { private Map<Slider, Consumer<Double>> initializeSliderMap() {
return Map.of( return Map.of(
frequencySlider, f -> audioPlayer.setFrequency(Math.pow(MAX_FREQUENCY, f)), frequencySlider, f -> audioPlayer.setBaseFrequency(Math.pow(MAX_FREQUENCY, f)),
rotateSpeedSlider, rotateEffect::setSpeed, rotateSpeedSlider, rotateEffect::setSpeed,
translationSpeedSlider, translateEffect::setSpeed, translationSpeedSlider, translateEffect::setSpeed,
scaleSlider, scaleEffect::setScale, scaleSlider, scaleEffect::setScale,
@ -631,7 +634,7 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
private void playNote(double frequency, double volume) { private void playNote(double frequency, double volume) {
frequencySlider.setValue(Math.log(frequency) / Math.log(MAX_FREQUENCY)); frequencySlider.setValue(Math.log(frequency) / Math.log(MAX_FREQUENCY));
audioPlayer.setFrequency(frequency); audioPlayer.setBaseFrequency(frequency);
scaleSlider.setValue(volume); scaleSlider.setValue(volume);
} }
@ -688,6 +691,19 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
} }
playNote(frequency, volume); 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);
} }
} }