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>
<artifactId>osci-render</artifactId>
<version>1.11.4</version>
<version>1.11.5</version>
<name>osci-render</name>

Wyświetl plik

@ -14,10 +14,14 @@ public interface AudioPlayer<S> 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);

Wyświetl plik

@ -44,6 +44,7 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
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<List<Shape>> {
}
@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<List<Shape>> {
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

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 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<Slider, Consumer<Double>> 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);
}
}