diff --git a/lib/ton.jar b/lib/ton.jar index 48dccc14..46567bf3 100644 Binary files a/lib/ton.jar and b/lib/ton.jar differ diff --git a/processing-library/ton/examples_ext/ExampleDSP04LowPassFilter/ExampleDSP04LowPassFilter.pde b/processing-library/ton/examples_ext/ExampleDSP04LowPassFilter/ExampleDSP04LowPassFilter.pde index 6a802f0f..868d79df 100644 --- a/processing-library/ton/examples_ext/ExampleDSP04LowPassFilter/ExampleDSP04LowPassFilter.pde +++ b/processing-library/ton/examples_ext/ExampleDSP04LowPassFilter/ExampleDSP04LowPassFilter.pde @@ -51,7 +51,7 @@ void audioblock(float[] pOutputSamples) { mSample = mButterworthLowPassFilter.filter(mSample); } mSample = DSP.clamp(mSample, -1, 1); - pOutputSamples[i] = mLPFilter.filter(mSample); + pOutputSamples[i] = mSample; } } interface filter_constants { @@ -59,13 +59,11 @@ interface filter_constants { float pi = (float) (2.0 * 0.707106781186547524401); } /** - * Second order Butterworth low-pass filter - * Dimitris Tassopoulos 2016 + * Second order Butterworth low-pass filter Dimitris Tassopoulos 2016 *
- * fc, corner frequency - * Butterworth low-pass and high-pass filters are specialized versions of the ordinary secondorder - * low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can - * assume before peaking in the frequency response is observed. + * fc, corner frequency Butterworth low-pass and high-pass filters are specialized versions of the ordinary + * secondorder low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can assume before + * peaking in the frequency response is observed. *
* from https://github.com/dimtass/DSP-Cpp-filters */ @@ -73,7 +71,7 @@ static class ButterworthLowPassFilter { float m_xnz1, m_xnz2, m_ynz1, m_ynz2; tp_coeffs m_coeffs = new tp_coeffs(); tp_coeffs calculate_coeffs(int fc) { - final int fs = 44100; + final int fs = DSP.DEFAULT_SAMPLING_RATE; float c = 1.0f / (tan(filter_constants.pi * fc / fs)); m_coeffs.a0 = 1.0f / (1.0f + filter_constants.sqrt2 * c + pow(c, 2.0f)); m_coeffs.a1 = 2.0f * m_coeffs.a0; @@ -84,7 +82,8 @@ static class ButterworthLowPassFilter { } float filter(float sample) { float xn = sample; - float yn = m_coeffs.a0 * xn + m_coeffs.a1 * m_xnz1 + m_coeffs.a2 * m_xnz2 - m_coeffs.b1 * m_ynz1 - m_coeffs.b2 * m_xnz2; + float yn = + m_coeffs.a0 * xn + m_coeffs.a1 * m_xnz1 + m_coeffs.a2 * m_xnz2 - m_coeffs.b1 * m_ynz1 - m_coeffs.b2 * m_xnz2; m_xnz2 = m_xnz1; m_xnz1 = xn; m_xnz2 = m_ynz1; @@ -93,11 +92,9 @@ static class ButterworthLowPassFilter { } } /** - * Second order Low-pass filter - * Dimitris Tassopoulos 2016 + * Second order Low-pass filter Dimitris Tassopoulos 2016 *
- * fc , corner frequency - * Q , quality factor controlling resonant peaking + * fc , corner frequency Q , quality factor controlling resonant peaking *
* from https://github.com/dimtass/DSP-Cpp-filters */ @@ -105,7 +102,7 @@ static class SecondOrderLowPassFilter { float m_xnz1, m_xnz2, m_ynz1, m_ynz2; tp_coeffs m_coeffs = new tp_coeffs(); tp_coeffs calculate_coeffs(float Q, int fc) { - final int fs = 44100; + final int fs = DSP.DEFAULT_SAMPLING_RATE; float w = 2.0f * filter_constants.pi * fc / fs; float d = 1.0f / Q; float b = 0.5f * (1.0f - (d / 2) * sin(w)) / (1.0f + (d / 2.0f) * sin(w)); diff --git a/processing-library/ton/examples_ext/ExampleDSP05Wavetable/ExampleDSP05Wavetable.pde b/processing-library/ton/examples_ext/ExampleDSP05Wavetable/ExampleDSP05Wavetable.pde new file mode 100644 index 00000000..631050d9 --- /dev/null +++ b/processing-library/ton/examples_ext/ExampleDSP05Wavetable/ExampleDSP05Wavetable.pde @@ -0,0 +1,111 @@ +import de.hfkbremen.ton.*; +import controlP5.*; +import ddf.minim.*; +import com.jsyn.unitgen.*; + + +final Wavetable mWavetable = new Wavetable(1024); +void settings() { + size(640, 480); +} +void setup() { + DSP.dumpAudioDevices(); + DSP.start(this); + triangle(mWavetable.wavetable()); +} +void draw() { + background(255); + DSP.draw_buffer(g, width, height); +} +void mouseMoved() { + mWavetable.set_frequency(map(mouseX, 0, width, 55, 220)); + mWavetable.set_amplitude(map(mouseY, 0, height, 0.0f, 0.9f)); +} +void keyPressed() { + switch (key) { + case '1': + sine(mWavetable.wavetable()); + break; + case '2': + sawtooth(mWavetable.wavetable()); + break; + case '3': + triangle(mWavetable.wavetable()); + break; + case '4': + square(mWavetable.wavetable()); + break; + case '5': + randomize(mWavetable.wavetable()); + break; + } +} +void audioblock(float[] pOutputSamples) { + for (int i = 0; i < pOutputSamples.length; i++) { + pOutputSamples[i] = mWavetable.process(); + } +} +void randomize(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = random(-1, 1); + } +} +static void sine(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = PApplet.sin(2.0f * PI * ((float) i / (float) (pWavetable.length))); + } +} +static void sawtooth(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = 2.0f * ((float) i / (float) (pWavetable.length - 1)) - 1.0f; + } +} +static void triangle(float[] pWavetable) { + final int q = pWavetable.length / 4; + final float qf = pWavetable.length * 0.25f; + for (int i = 0; i < q; i++) { + pWavetable[i] = i / qf; + pWavetable[i + (q * 1)] = (qf - i) / qf; + pWavetable[i + (q * 2)] = -i / qf; + pWavetable[i + (q * 3)] = -(qf - i) / qf; + } +} +static void square(float[] pWavetable) { + for (int i = 0; i < pWavetable.length / 2; i++) { + pWavetable[i] = 1.0f; + pWavetable[i + pWavetable.length / 2] = -1.0f; + } +} +static class Wavetable { + final float[] mWavetable; + float mFrequency; + float mStepSize; + float mArrayPtr; + float mAmplitude; + Wavetable(int pWavetableSize) { + mWavetable = new float[pWavetableSize]; + mArrayPtr = 0; + mAmplitude = 0.75f; + set_frequency(220); + } + void set_frequency(float pFrequency) { + if (mFrequency != pFrequency) { + mFrequency = pFrequency; + mStepSize = mFrequency * ((float) mWavetable.length / (float) DSP.DEFAULT_SAMPLING_RATE); + } + } + void set_amplitude(float pAmplitude) { + mAmplitude = pAmplitude; + } + float[] wavetable() { + return mWavetable; + } + float process() { + mArrayPtr += mStepSize; + final int i = (int) mArrayPtr; + final float mFrac = mArrayPtr - i; + final int j = i % mWavetable.length; + mArrayPtr = j + mFrac; + return mWavetable[j] * mAmplitude; + } +} diff --git a/processing-library/ton/examples_ext/ExampleInstruments01ADSR/ExampleInstruments01ADSR.pde b/processing-library/ton/examples_ext/ExampleInstruments01ADSR/ExampleInstruments01ADSR.pde index 13380462..538406fa 100644 --- a/processing-library/ton/examples_ext/ExampleInstruments01ADSR/ExampleInstruments01ADSR.pde +++ b/processing-library/ton/examples_ext/ExampleInstruments01ADSR/ExampleInstruments01ADSR.pde @@ -4,21 +4,21 @@ import ddf.minim.*; import com.jsyn.unitgen.*; -Parameter mParameterAttack; -Parameter mParameterDecay; -Parameter mParameterSustain; -Parameter mParameterRelease; +Slider mSliderAttack; +Slider mSliderDecay; +Slider mSliderSustain; +Slider mSliderRelease; int mNote; void settings() { - size(640, 480, P2D); + size(640, 480); } void setup() { hint(DISABLE_KEY_REPEAT); - mParameterAttack = new Parameter(); - mParameterDecay = new Parameter(); - mParameterSustain = new Parameter(); - mParameterSustain.horziontal = false; - mParameterRelease = new Parameter(); + mSliderAttack = new Slider(); + mSliderDecay = new Slider(); + mSliderSustain = new Slider(); + mSliderSustain.horziontal = false; + mSliderRelease = new Slider(); updateADSR(); println(Instrument.ADSR_DIAGRAM); } @@ -29,7 +29,7 @@ void draw() { } else { background(255); } - final float mXOffset = (width - Parameter.size * 4) * 0.5f; + final float mXOffset = (width - Slider.size * 4) * 0.5f; final float mYOffset = height * 0.5f; translate(mXOffset, mYOffset); scale(1, -1); @@ -47,64 +47,68 @@ void keyReleased() { Ton.note_off(); } void drawDiagram() { - stroke(0, 63); - line(0, 0, Parameter.size * 4, 0); - stroke(0); - line(0, 0, - mParameterAttack.current_position_x(), - mParameterAttack.current_position_y()); - line(mParameterAttack.current_position_x(), - mParameterAttack.current_position_y(), - mParameterDecay.current_position_x(), - mParameterDecay.current_position_y()); - line(mParameterDecay.current_position_x(), - mParameterDecay.current_position_y(), - mParameterSustain.current_position_x(), - mParameterSustain.current_position_y()); - line(mParameterSustain.current_position_x(), - mParameterSustain.current_position_y(), - mParameterRelease.current_position_x(), - mParameterRelease.current_position_y()); + stroke(0, 15); + line(0, 0, Slider.size * 4, 0); + /* GUI */ noStroke(); fill(0); - ellipse(0, 0, Parameter.radius * 2, Parameter.radius * 2); - mParameterAttack.draw(g); - mParameterDecay.draw(g); - mParameterSustain.draw(g); - mParameterRelease.draw(g); + ellipse(0, 0, Slider.radius, Slider.radius); + mSliderAttack.draw(g); + mSliderDecay.draw(g); + mSliderSustain.draw(g); + mSliderRelease.draw(g); + /* envelope */ + strokeWeight(3); + stroke(0); + line(0, 0, + mSliderAttack.current_position_x(), + mSliderAttack.current_position_y()); + line(mSliderAttack.current_position_x(), + mSliderAttack.current_position_y(), + mSliderDecay.current_position_x(), + mSliderDecay.current_position_y()); + line(mSliderDecay.current_position_x(), + mSliderDecay.current_position_y(), + mSliderSustain.current_position_x(), + mSliderSustain.current_position_y()); + line(mSliderSustain.current_position_x(), + mSliderSustain.current_position_y(), + mSliderRelease.current_position_x(), + mSliderRelease.current_position_y()); + strokeWeight(1); } void updateDiagram(float mXOffset, float mYOffset) { float mX = mouseX - mXOffset; float mY = -mouseY + mYOffset; - mParameterAttack.update(mX, mY, mousePressed); - mParameterAttack.x = 0; - mParameterAttack.y = Parameter.size; - mParameterDecay.update(mX, mY, mousePressed); - mParameterDecay.x = mParameterAttack.current_position_x(); - mParameterDecay.y = mParameterSustain.current_position_y(); - mParameterSustain.update(mX, mY, mousePressed); - mParameterSustain.x = mParameterDecay.current_position_x() + Parameter.size; - mParameterSustain.y = 0; - mParameterRelease.update(mX, mY, mousePressed); - mParameterRelease.x = mParameterSustain.x; - mParameterRelease.y = 0; + mSliderAttack.update(mX, mY, mousePressed); + mSliderAttack.x = 0; + mSliderAttack.y = Slider.size; + mSliderDecay.update(mX, mY, mousePressed); + mSliderDecay.x = mSliderAttack.current_position_x(); + mSliderDecay.y = mSliderSustain.current_position_y(); + mSliderSustain.update(mX, mY, mousePressed); + mSliderSustain.x = mSliderDecay.current_position_x() + Slider.size; + mSliderSustain.y = 0; + mSliderRelease.update(mX, mY, mousePressed); + mSliderRelease.x = mSliderSustain.x; + mSliderRelease.y = 0; } void updateADSR() { - Ton.instrument().attack(mParameterAttack.value); - Ton.instrument().decay(mParameterDecay.value); - Ton.instrument().sustain(mParameterSustain.value); - Ton.instrument().release(mParameterRelease.value); + Ton.instrument().attack(mSliderAttack.value); + Ton.instrument().decay(mSliderDecay.value); + Ton.instrument().sustain(mSliderSustain.value); + Ton.instrument().release(mSliderRelease.value); } -static class Parameter { +static class Slider { static final float size = 120; - static final float radius = 6; + static final float radius = 8; float x; float y; float value; boolean horziontal; boolean hoover; boolean drag; - Parameter() { + Slider() { x = 0; y = 0; value = 0.5f; @@ -113,11 +117,16 @@ static class Parameter { drag = false; } void draw(PGraphics g) { -// g.stroke(0, 63); -// g.line(x, y, x + (horziontal ? size : 0), y + (horziontal ? 0 : size)); + g.stroke(191); + g.line(x, y, x + (horziontal ? size : 0), y + (horziontal ? 0 : size)); + final float mEdgeDiameter = 5; + g.noStroke(); + g.fill(0); + g.ellipse(x, y, mEdgeDiameter, mEdgeDiameter); + g.ellipse(x + (horziontal ? size : 0), y + (horziontal ? 0 : size), mEdgeDiameter, mEdgeDiameter); g.noStroke(); - g.fill(hoover ? (drag ? 223 : 127) : 0); - g.ellipse(current_position_x(), current_position_y(), radius * 2, radius * 2); + g.fill(0); + g.ellipse(current_position_x(), current_position_y(), radius * (hoover ? 2 : 1), radius * (hoover ? 2 : 1)); } void update_value(float pX, float pY) { value = (horziontal ? (pX - x) : (pY - y)) / size; @@ -125,7 +134,7 @@ static class Parameter { } boolean hit(float pX, float pY) { final float mDistance = PVector.dist(new PVector().set(pX, pY), - new PVector().set(current_position_x(), current_position_y())); + new PVector().set(current_position_x(), current_position_y())); return mDistance < radius * 2; } float current_position_x() { diff --git a/processing-library/ton/library/ton.jar b/processing-library/ton/library/ton.jar index 48dccc14..46567bf3 100644 Binary files a/processing-library/ton/library/ton.jar and b/processing-library/ton/library/ton.jar differ diff --git a/processing-library/ton/src/de/hfkbremen/ton/AudioBufferManager.java b/processing-library/ton/src/de/hfkbremen/ton/AudioBufferManager.java index dc5fe986..65c139d7 100644 --- a/processing-library/ton/src/de/hfkbremen/ton/AudioBufferManager.java +++ b/processing-library/ton/src/de/hfkbremen/ton/AudioBufferManager.java @@ -6,6 +6,9 @@ import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.TargetDataLine; +import static de.hfkbremen.ton.DSP.DEFAULT_AUDIOBLOCK_SIZE; +import static de.hfkbremen.ton.DSP.DEFAULT_SAMPLING_RATE; + public class AudioBufferManager extends Thread { /* @@ -38,16 +41,16 @@ public class AudioBufferManager extends Thread { private boolean mRunBuffer = true; public AudioBufferManager(AudioBufferRenderer pSampleRenderer) { - this(pSampleRenderer, 44100, 512, DEFAULT, STEREO, DEFAULT, MONO); + this(pSampleRenderer, DEFAULT_SAMPLING_RATE, DEFAULT_AUDIOBLOCK_SIZE, DEFAULT, STEREO, DEFAULT, MONO); } public AudioBufferManager(AudioBufferRenderer pSampleRenderer, - int pSampleRate, - int pSampleBufferSize, - int pOutputDevice, - int pNumOutputChannels, - int pInputDevice, - int pNumInputChannels) { + int pSampleRate, + int pSampleBufferSize, + int pOutputDevice, + int pNumOutputChannels, + int pInputDevice, + int pNumInputChannels) { mSampleRenderer = pSampleRenderer; mSampleRate = pSampleRate; mSampleBufferSize = pSampleBufferSize; @@ -57,16 +60,16 @@ public AudioBufferManager(AudioBufferRenderer pSampleRenderer, try { /* output */ final AudioFormat mOutputFormat = new AudioFormat(pSampleRate, - BITS_PER_SAMPLE, - pNumOutputChannels, - SIGNED, - LITTLE_ENDIAN); + BITS_PER_SAMPLE, + pNumOutputChannels, + SIGNED, + LITTLE_ENDIAN); if (pOutputDevice == DEFAULT) { mOutputLine = AudioSystem.getSourceDataLine(mOutputFormat); } else { System.out.println("+ OUTPUT DEVICE: " + AudioSystem.getMixerInfo()[pOutputDevice]); mOutputLine = AudioSystem.getSourceDataLine(mOutputFormat, - AudioSystem.getMixerInfo()[pOutputDevice]); + AudioSystem.getMixerInfo()[pOutputDevice]); } final int BYTES_PER_SAMPLE = BITS_PER_SAMPLE / 8; mOutputByteBuffer = new byte[mSampleBufferSize * BYTES_PER_SAMPLE * pNumOutputChannels]; @@ -75,15 +78,15 @@ public AudioBufferManager(AudioBufferRenderer pSampleRenderer, /* input */ if (mNumInputChannels > 0) { final AudioFormat mInputFormat = new AudioFormat(pSampleRate, - BITS_PER_SAMPLE, - mNumInputChannels, - SIGNED, - LITTLE_ENDIAN); + BITS_PER_SAMPLE, + mNumInputChannels, + SIGNED, + LITTLE_ENDIAN); if (pInputDevice == DEFAULT) { mInputLine = AudioSystem.getTargetDataLine(mInputFormat); } else { mInputLine = AudioSystem.getTargetDataLine(mInputFormat, - AudioSystem.getMixerInfo()[pInputDevice]); + AudioSystem.getMixerInfo()[pInputDevice]); System.out.println("+ INPUT DEVICE: " + AudioSystem.getMixerInfo()[pInputDevice]); } mInputByteBuffer = new byte[mSampleBufferSize * BYTES_PER_SAMPLE * mNumInputChannels]; @@ -92,7 +95,9 @@ public AudioBufferManager(AudioBufferRenderer pSampleRenderer, } catch (LineUnavailableException e) { System.err.println(e.getMessage()); } - if (mInputLine != null) { mInputLine.start(); } + if (mInputLine != null) { + mInputLine.start(); + } mOutputLine.start(); start(); } diff --git a/processing-library/ton/src/de/hfkbremen/ton/DSP.java b/processing-library/ton/src/de/hfkbremen/ton/DSP.java index 09518ed5..48c641e8 100644 --- a/processing-library/ton/src/de/hfkbremen/ton/DSP.java +++ b/processing-library/ton/src/de/hfkbremen/ton/DSP.java @@ -1,6 +1,7 @@ package de.hfkbremen.ton; import processing.core.PApplet; +import processing.core.PGraphics; import javax.sound.sampled.AudioSystem; import java.lang.reflect.InvocationTargetException; @@ -10,6 +11,8 @@ public class DSP implements AudioBufferRenderer { + public static final int DEFAULT_SAMPLING_RATE = 44100; + public static final int DEFAULT_AUDIOBLOCK_SIZE = 512; private static final String METHOD_NAME = "audioblock"; private static AudioBufferManager mAudioPlayer; private static DSP mInstance = null; @@ -27,28 +30,52 @@ public DSP(PApplet pPApplet, int pNumberOutputChannels, int pNumberInputChannels try { if (mNumberOutputChannels == 2 && mNumberInputChannels == 2) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class, - float[].class, - float[].class, - float[].class); + float[].class, + float[].class, + float[].class, + float[].class); } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 0) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class, - float[].class); + float[].class, + float[].class); } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 1) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class, - float[].class); + float[].class, + float[].class); } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 0) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class); + float[].class); } else { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[][].class); + float[][].class); } } catch (NoSuchMethodException | SecurityException ex) { System.err.println("+++ @DSP / could not find callback `" + METHOD_NAME + "()`. hint: check the callback " + - "method paramters, they must match the sum of input and ouput channels. default is `" + METHOD_NAME + "(float[])` ( = MONO OUTPUT )."); + "method paramters, they must match the sum of input and ouput channels. default is `" + METHOD_NAME + "(float[])` ( = MONO OUTPUT )."); + } + } + + public void audioblock(float[][] pOutputSamples, float[][] pInputSamples) { + try { + if (mNumberOutputChannels == 1 && mNumberInputChannels == 0) { + mMethod.invoke(mPApplet, pOutputSamples[0]); + mCurrentBufferLeft = pOutputSamples[0]; + } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 1) { + mMethod.invoke(mPApplet, pOutputSamples[0], pInputSamples[0]); + mCurrentBufferLeft = pOutputSamples[0]; + } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 0) { + mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1]); + mCurrentBufferLeft = pOutputSamples[0]; + mCurrentBufferRight = pOutputSamples[1]; + } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 2) { + mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1], pInputSamples[0], pInputSamples[1]); + mCurrentBufferLeft = pOutputSamples[0]; + mCurrentBufferRight = pOutputSamples[1]; + } else { + mMethod.invoke(mPApplet, pOutputSamples); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + ex.printStackTrace(); } } @@ -65,19 +92,19 @@ public static DSP start(PApplet pPApplet, int pNumberOutputChannels, int pNumber } public static DSP start(PApplet pPApplet, - int pOutputDevice, - int pNumberOutputChannels, - int pInputDevice, - int pNumberInputChannels) { + int pOutputDevice, + int pNumberOutputChannels, + int pInputDevice, + int pNumberInputChannels) { if (mInstance == null) { mInstance = new DSP(pPApplet, pNumberOutputChannels, pNumberInputChannels); mAudioPlayer = new AudioBufferManager(mInstance, - 44100, - 512, - pOutputDevice, - pNumberOutputChannels, - pInputDevice, - pNumberInputChannels); + DEFAULT_SAMPLING_RATE, + DEFAULT_AUDIOBLOCK_SIZE, + pOutputDevice, + pNumberOutputChannels, + pInputDevice, + pNumberInputChannels); } return mInstance; } @@ -134,27 +161,19 @@ public static float flip(float pValue) { } } - public void audioblock(float[][] pOutputSamples, float[][] pInputSamples) { - try { - if (mNumberOutputChannels == 1 && mNumberInputChannels == 0) { - mMethod.invoke(mPApplet, pOutputSamples[0]); - mCurrentBufferLeft = pOutputSamples[0]; - } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 1) { - mMethod.invoke(mPApplet, pOutputSamples[0], pInputSamples[0]); - mCurrentBufferLeft = pOutputSamples[0]; - } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 0) { - mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1]); - mCurrentBufferLeft = pOutputSamples[0]; - mCurrentBufferRight = pOutputSamples[1]; - } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 2) { - mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1], pInputSamples[0], pInputSamples[1]); - mCurrentBufferLeft = pOutputSamples[0]; - mCurrentBufferRight = pOutputSamples[1]; - } else { - mMethod.invoke(mPApplet, pOutputSamples); + public static void draw_buffer(PGraphics g, int pWidth, int pHeight) { + final int mBufferSize = DSP.buffer_size(); + if (DSP.buffer() != null) { +// g.beginShape(PConstants.LINES); + for (int i = 0; i < mBufferSize - 1; i++) { +// final float x = PApplet.map(i, 0, mBufferSize, 0, pWidth); +// g.vertex(x, PApplet.map(DSP.buffer()[i], -1, 1, 0, pHeight)); + g.line(PApplet.map(i, 0, mBufferSize, 0, pWidth), + PApplet.map(DSP.buffer()[i], -1, 1, 0, pHeight), + PApplet.map(i + 1, 0, mBufferSize, 0, pWidth), + PApplet.map(DSP.buffer()[i + 1], -1, 1, 0, pHeight)); } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - ex.printStackTrace(); +// g.endShape(); } } } diff --git a/processing-library/ton/src/de/hfkbremen/ton/ToneEngineJSyn.java b/processing-library/ton/src/de/hfkbremen/ton/ToneEngineJSyn.java index 953185f2..4858d497 100755 --- a/processing-library/ton/src/de/hfkbremen/ton/ToneEngineJSyn.java +++ b/processing-library/ton/src/de/hfkbremen/ton/ToneEngineJSyn.java @@ -9,6 +9,7 @@ import java.util.Timer; import java.util.TimerTask; +import static de.hfkbremen.ton.DSP.DEFAULT_SAMPLING_RATE; import static de.hfkbremen.ton.Note.note_to_frequency; import static de.hfkbremen.ton.Ton.clamp127; import static processing.core.PApplet.constrain; @@ -29,11 +30,11 @@ public ToneEngineJSyn() { } public ToneEngineJSyn(int pDefaultInstrumentType) { - this(pDefaultInstrumentType, 44100, DEFAULT_DEVICE, 1, DEFAULT_DEVICE, 2); + this(pDefaultInstrumentType, DEFAULT_SAMPLING_RATE, DEFAULT_DEVICE, 1, DEFAULT_DEVICE, 2); } public ToneEngineJSyn(int pDefaultInstrumentType, int pOutputDeviceID, int pOutputChannels) { - this(pDefaultInstrumentType, 44100, DEFAULT_DEVICE, 1, pOutputDeviceID, pOutputChannels); + this(pDefaultInstrumentType, DEFAULT_SAMPLING_RATE, DEFAULT_DEVICE, 1, pOutputDeviceID, pOutputChannels); } public ToneEngineJSyn(int pDefaultInstrumentType, diff --git a/processing-library/ton/src/de/hfkbremen/ton/Wavetable.java b/processing-library/ton/src/de/hfkbremen/ton/Wavetable.java new file mode 100644 index 00000000..02e21f5b --- /dev/null +++ b/processing-library/ton/src/de/hfkbremen/ton/Wavetable.java @@ -0,0 +1,79 @@ +package de.hfkbremen.ton; + +import processing.core.PApplet; + +public class Wavetable { + + private final int mSamplingRate; + private final float[] mWavetable; + private float mFrequency; + private float mStepSize; + private float mArrayPtr; + private float mAmplitude; + + public Wavetable(int pWavetableSize) { + this(pWavetableSize, DSP.DEFAULT_SAMPLING_RATE); + } + + public Wavetable(int pWavetableSize, int pSamplingRate) { + mWavetable = new float[pWavetableSize]; + mSamplingRate = pSamplingRate; + mArrayPtr = 0; + mAmplitude = 0.75f; + set_frequency(220); + } + + public void set_frequency(float pFrequency) { + if (mFrequency != pFrequency) { + mFrequency = pFrequency; + mStepSize = mFrequency * ((float) mWavetable.length / (float) mSamplingRate); + } + } + + public void set_amplitude(float pAmplitude) { + mAmplitude = pAmplitude; + } + + public float[] wavetable() { + return mWavetable; + } + + public float process() { + mArrayPtr += mStepSize; + final int i = (int) mArrayPtr; + final float mFrac = mArrayPtr - i; + final int j = i % mWavetable.length; + mArrayPtr = j + mFrac; + return mWavetable[j] * mAmplitude; + } + + public static void sine(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = PApplet.sin(2.0f * PApplet.PI * ((float) i / (float) (pWavetable.length))); + } + } + + public static void sawtooth(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = 2.0f * ((float) i / (float) (pWavetable.length - 1)) - 1.0f; + } + } + + public static void triangle(float[] pWavetable) { + final int q = pWavetable.length / 4; + final float qf = pWavetable.length * 0.25f; + for (int i = 0; i < q; i++) { + pWavetable[i] = i / qf; + pWavetable[i + (q * 1)] = (qf - i) / qf; + pWavetable[i + (q * 2)] = -i / qf; + pWavetable[i + (q * 3)] = -(qf - i) / qf; + } + } + + public static void square(float[] pWavetable) { + for (int i = 0; i < pWavetable.length / 2; i++) { + pWavetable[i] = 1.0f; + pWavetable[i + pWavetable.length / 2] = -1.0f; + } + } +} \ No newline at end of file diff --git a/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java b/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java index dd82e9aa..8150e9cd 100644 --- a/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java +++ b/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java @@ -63,7 +63,7 @@ public void audioblock(float[] pOutputSamples) { mSample = mButterworthLowPassFilter.filter(mSample); } mSample = DSP.clamp(mSample, -1, 1); - pOutputSamples[i] = mLPFilter.filter(mSample); + pOutputSamples[i] = mSample; } } @@ -75,13 +75,11 @@ private interface filter_constants { /** - * Second order Butterworth low-pass filter - * Dimitris Tassopoulos 2016 + * Second order Butterworth low-pass filter Dimitris Tassopoulos 2016 *
- * fc, corner frequency - * Butterworth low-pass and high-pass filters are specialized versions of the ordinary secondorder - * low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can - * assume before peaking in the frequency response is observed. + * fc, corner frequency Butterworth low-pass and high-pass filters are specialized versions of the ordinary + * secondorder low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can assume before + * peaking in the frequency response is observed. *
* from https://github.com/dimtass/DSP-Cpp-filters */ @@ -91,7 +89,7 @@ private static class ButterworthLowPassFilter { tp_coeffs m_coeffs = new tp_coeffs(); public tp_coeffs calculate_coeffs(int fc) { - final int fs = 44100; + final int fs = DSP.DEFAULT_SAMPLING_RATE; float c = 1.0f / (tan(filter_constants.pi * fc / fs)); m_coeffs.a0 = 1.0f / (1.0f + filter_constants.sqrt2 * c + pow(c, 2.0f)); m_coeffs.a1 = 2.0f * m_coeffs.a0; @@ -103,7 +101,8 @@ public tp_coeffs calculate_coeffs(int fc) { public float filter(float sample) { float xn = sample; - float yn = m_coeffs.a0 * xn + m_coeffs.a1 * m_xnz1 + m_coeffs.a2 * m_xnz2 - m_coeffs.b1 * m_ynz1 - m_coeffs.b2 * m_xnz2; + float yn = + m_coeffs.a0 * xn + m_coeffs.a1 * m_xnz1 + m_coeffs.a2 * m_xnz2 - m_coeffs.b1 * m_ynz1 - m_coeffs.b2 * m_xnz2; m_xnz2 = m_xnz1; m_xnz1 = xn; @@ -116,11 +115,9 @@ public float filter(float sample) { } /** - * Second order Low-pass filter - * Dimitris Tassopoulos 2016 + * Second order Low-pass filter Dimitris Tassopoulos 2016 *
- * fc , corner frequency - * Q , quality factor controlling resonant peaking + * fc , corner frequency Q , quality factor controlling resonant peaking *
* from https://github.com/dimtass/DSP-Cpp-filters */ @@ -130,7 +127,7 @@ private static class SecondOrderLowPassFilter { tp_coeffs m_coeffs = new tp_coeffs(); public tp_coeffs calculate_coeffs(float Q, int fc) { - final int fs = 44100; + final int fs = DSP.DEFAULT_SAMPLING_RATE; float w = 2.0f * filter_constants.pi * fc / fs; float d = 1.0f / Q; float b = 0.5f * (1.0f - (d / 2) * sin(w)) / (1.0f + (d / 2.0f) * sin(w)); diff --git a/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP05Wavetable.java b/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP05Wavetable.java new file mode 100644 index 00000000..cb6661a1 --- /dev/null +++ b/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP05Wavetable.java @@ -0,0 +1,135 @@ +package de.hfkbremen.ton.examples_ext; + +import de.hfkbremen.ton.DSP; +import processing.core.PApplet; + +public class SketchExampleDSP05Wavetable extends PApplet { + + private final Wavetable mWavetable = new Wavetable(1024); + + public void settings() { + size(640, 480); + } + + public void setup() { + DSP.dumpAudioDevices(); + DSP.start(this); + triangle(mWavetable.wavetable()); + } + + public void draw() { + background(255); + DSP.draw_buffer(g, width, height); + } + + public void mouseMoved() { + mWavetable.set_frequency(map(mouseX, 0, width, 55, 220)); + mWavetable.set_amplitude(map(mouseY, 0, height, 0.0f, 0.9f)); + } + + public void keyPressed() { + switch (key) { + case '1': + sine(mWavetable.wavetable()); + break; + case '2': + sawtooth(mWavetable.wavetable()); + break; + case '3': + triangle(mWavetable.wavetable()); + break; + case '4': + square(mWavetable.wavetable()); + break; + case '5': + randomize(mWavetable.wavetable()); + break; + } + } + + public void audioblock(float[] pOutputSamples) { + for (int i = 0; i < pOutputSamples.length; i++) { + pOutputSamples[i] = mWavetable.process(); + } + } + + private void randomize(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = random(-1, 1); + } + } + + public static void sine(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = PApplet.sin(2.0f * PI * ((float) i / (float) (pWavetable.length))); + } + } + + public static void sawtooth(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = 2.0f * ((float) i / (float) (pWavetable.length - 1)) - 1.0f; + } + } + + public static void triangle(float[] pWavetable) { + final int q = pWavetable.length / 4; + final float qf = pWavetable.length * 0.25f; + for (int i = 0; i < q; i++) { + pWavetable[i] = i / qf; + pWavetable[i + (q * 1)] = (qf - i) / qf; + pWavetable[i + (q * 2)] = -i / qf; + pWavetable[i + (q * 3)] = -(qf - i) / qf; + } + } + + public static void square(float[] pWavetable) { + for (int i = 0; i < pWavetable.length / 2; i++) { + pWavetable[i] = 1.0f; + pWavetable[i + pWavetable.length / 2] = -1.0f; + } + } + + private static class Wavetable { + + private final float[] mWavetable; + private float mFrequency; + private float mStepSize; + private float mArrayPtr; + private float mAmplitude; + + public Wavetable(int pWavetableSize) { + mWavetable = new float[pWavetableSize]; + mArrayPtr = 0; + mAmplitude = 0.75f; + set_frequency(220); + } + + public void set_frequency(float pFrequency) { + if (mFrequency != pFrequency) { + mFrequency = pFrequency; + mStepSize = mFrequency * ((float) mWavetable.length / (float) DSP.DEFAULT_SAMPLING_RATE); + } + } + + public void set_amplitude(float pAmplitude) { + mAmplitude = pAmplitude; + } + + public float[] wavetable() { + return mWavetable; + } + + public float process() { + mArrayPtr += mStepSize; + final int i = (int) mArrayPtr; + final float mFrac = mArrayPtr - i; + final int j = i % mWavetable.length; + mArrayPtr = j + mFrac; + return mWavetable[j] * mAmplitude; + } + } + + public static void main(String[] args) { + PApplet.main(SketchExampleDSP05Wavetable.class.getName()); + } +} \ No newline at end of file diff --git a/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java b/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java index 728c001f..73467763 100755 --- a/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java +++ b/processing-library/ton/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java @@ -26,24 +26,24 @@ */ public class SketchExampleInstruments01ADSR extends PApplet { - private Parameter mParameterAttack; - private Parameter mParameterDecay; - private Parameter mParameterSustain; - private Parameter mParameterRelease; + private Slider mSliderAttack; + private Slider mSliderDecay; + private Slider mSliderSustain; + private Slider mSliderRelease; private int mNote; public void settings() { - size(640, 480, P2D); + size(640, 480); } public void setup() { hint(DISABLE_KEY_REPEAT); - mParameterAttack = new Parameter(); - mParameterDecay = new Parameter(); - mParameterSustain = new Parameter(); - mParameterSustain.horziontal = false; - mParameterRelease = new Parameter(); + mSliderAttack = new Slider(); + mSliderDecay = new Slider(); + mSliderSustain = new Slider(); + mSliderSustain.horziontal = false; + mSliderRelease = new Slider(); updateADSR(); println(Instrument.ADSR_DIAGRAM); } @@ -55,7 +55,7 @@ public void draw() { } else { background(255); } - final float mXOffset = (width - Parameter.size * 4) * 0.5f; + final float mXOffset = (width - Slider.size * 4) * 0.5f; final float mYOffset = height * 0.5f; translate(mXOffset, mYOffset); scale(1, -1); @@ -78,68 +78,72 @@ public void keyReleased() { } private void drawDiagram() { - stroke(0, 63); - line(0, 0, Parameter.size * 4, 0); - - stroke(0); - line(0, 0, - mParameterAttack.current_position_x(), - mParameterAttack.current_position_y()); - line(mParameterAttack.current_position_x(), - mParameterAttack.current_position_y(), - mParameterDecay.current_position_x(), - mParameterDecay.current_position_y()); - line(mParameterDecay.current_position_x(), - mParameterDecay.current_position_y(), - mParameterSustain.current_position_x(), - mParameterSustain.current_position_y()); - line(mParameterSustain.current_position_x(), - mParameterSustain.current_position_y(), - mParameterRelease.current_position_x(), - mParameterRelease.current_position_y()); + stroke(0, 15); + line(0, 0, Slider.size * 4, 0); + /* GUI */ noStroke(); fill(0); - ellipse(0, 0, Parameter.radius * 2, Parameter.radius * 2); + ellipse(0, 0, Slider.radius, Slider.radius); + + mSliderAttack.draw(g); + mSliderDecay.draw(g); + mSliderSustain.draw(g); + mSliderRelease.draw(g); - mParameterAttack.draw(g); - mParameterDecay.draw(g); - mParameterSustain.draw(g); - mParameterRelease.draw(g); + /* envelope */ + strokeWeight(3); + stroke(0); + line(0, 0, + mSliderAttack.current_position_x(), + mSliderAttack.current_position_y()); + line(mSliderAttack.current_position_x(), + mSliderAttack.current_position_y(), + mSliderDecay.current_position_x(), + mSliderDecay.current_position_y()); + line(mSliderDecay.current_position_x(), + mSliderDecay.current_position_y(), + mSliderSustain.current_position_x(), + mSliderSustain.current_position_y()); + line(mSliderSustain.current_position_x(), + mSliderSustain.current_position_y(), + mSliderRelease.current_position_x(), + mSliderRelease.current_position_y()); + strokeWeight(1); } private void updateDiagram(float mXOffset, float mYOffset) { float mX = mouseX - mXOffset; float mY = -mouseY + mYOffset; - mParameterAttack.update(mX, mY, mousePressed); - mParameterAttack.x = 0; - mParameterAttack.y = Parameter.size; + mSliderAttack.update(mX, mY, mousePressed); + mSliderAttack.x = 0; + mSliderAttack.y = Slider.size; - mParameterDecay.update(mX, mY, mousePressed); - mParameterDecay.x = mParameterAttack.current_position_x(); - mParameterDecay.y = mParameterSustain.current_position_y(); + mSliderDecay.update(mX, mY, mousePressed); + mSliderDecay.x = mSliderAttack.current_position_x(); + mSliderDecay.y = mSliderSustain.current_position_y(); - mParameterSustain.update(mX, mY, mousePressed); - mParameterSustain.x = mParameterDecay.current_position_x() + Parameter.size; - mParameterSustain.y = 0; + mSliderSustain.update(mX, mY, mousePressed); + mSliderSustain.x = mSliderDecay.current_position_x() + Slider.size; + mSliderSustain.y = 0; - mParameterRelease.update(mX, mY, mousePressed); - mParameterRelease.x = mParameterSustain.x; - mParameterRelease.y = 0; + mSliderRelease.update(mX, mY, mousePressed); + mSliderRelease.x = mSliderSustain.x; + mSliderRelease.y = 0; } private void updateADSR() { - Ton.instrument().attack(mParameterAttack.value); - Ton.instrument().decay(mParameterDecay.value); - Ton.instrument().sustain(mParameterSustain.value); - Ton.instrument().release(mParameterRelease.value); + Ton.instrument().attack(mSliderAttack.value); + Ton.instrument().decay(mSliderDecay.value); + Ton.instrument().sustain(mSliderSustain.value); + Ton.instrument().release(mSliderRelease.value); } - private static class Parameter { + private static class Slider { private static final float size = 120; - private static final float radius = 6; + private static final float radius = 8; float x; float y; float value; @@ -147,7 +151,7 @@ private static class Parameter { boolean hoover; boolean drag; - public Parameter() { + public Slider() { x = 0; y = 0; value = 0.5f; @@ -157,12 +161,18 @@ public Parameter() { } void draw(PGraphics g) { -// g.stroke(0, 63); -// g.line(x, y, x + (horziontal ? size : 0), y + (horziontal ? 0 : size)); + g.stroke(191); + g.line(x, y, x + (horziontal ? size : 0), y + (horziontal ? 0 : size)); + + final float mEdgeDiameter = 5; + g.noStroke(); + g.fill(0); + g.ellipse(x, y, mEdgeDiameter, mEdgeDiameter); + g.ellipse(x + (horziontal ? size : 0), y + (horziontal ? 0 : size), mEdgeDiameter, mEdgeDiameter); g.noStroke(); - g.fill(hoover ? (drag ? 223 : 127) : 0); - g.ellipse(current_position_x(), current_position_y(), radius * 2, radius * 2); + g.fill(0); + g.ellipse(current_position_x(), current_position_y(), radius * (hoover ? 2 : 1), radius * (hoover ? 2 : 1)); } void update_value(float pX, float pY) { @@ -172,7 +182,7 @@ void update_value(float pX, float pY) { boolean hit(float pX, float pY) { final float mDistance = PVector.dist(new PVector().set(pX, pY), - new PVector().set(current_position_x(), current_position_y())); + new PVector().set(current_position_x(), current_position_y())); return mDistance < radius * 2; } diff --git a/src/de/hfkbremen/ton/AudioBufferManager.java b/src/de/hfkbremen/ton/AudioBufferManager.java index dc5fe986..65c139d7 100644 --- a/src/de/hfkbremen/ton/AudioBufferManager.java +++ b/src/de/hfkbremen/ton/AudioBufferManager.java @@ -6,6 +6,9 @@ import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.TargetDataLine; +import static de.hfkbremen.ton.DSP.DEFAULT_AUDIOBLOCK_SIZE; +import static de.hfkbremen.ton.DSP.DEFAULT_SAMPLING_RATE; + public class AudioBufferManager extends Thread { /* @@ -38,16 +41,16 @@ public class AudioBufferManager extends Thread { private boolean mRunBuffer = true; public AudioBufferManager(AudioBufferRenderer pSampleRenderer) { - this(pSampleRenderer, 44100, 512, DEFAULT, STEREO, DEFAULT, MONO); + this(pSampleRenderer, DEFAULT_SAMPLING_RATE, DEFAULT_AUDIOBLOCK_SIZE, DEFAULT, STEREO, DEFAULT, MONO); } public AudioBufferManager(AudioBufferRenderer pSampleRenderer, - int pSampleRate, - int pSampleBufferSize, - int pOutputDevice, - int pNumOutputChannels, - int pInputDevice, - int pNumInputChannels) { + int pSampleRate, + int pSampleBufferSize, + int pOutputDevice, + int pNumOutputChannels, + int pInputDevice, + int pNumInputChannels) { mSampleRenderer = pSampleRenderer; mSampleRate = pSampleRate; mSampleBufferSize = pSampleBufferSize; @@ -57,16 +60,16 @@ public AudioBufferManager(AudioBufferRenderer pSampleRenderer, try { /* output */ final AudioFormat mOutputFormat = new AudioFormat(pSampleRate, - BITS_PER_SAMPLE, - pNumOutputChannels, - SIGNED, - LITTLE_ENDIAN); + BITS_PER_SAMPLE, + pNumOutputChannels, + SIGNED, + LITTLE_ENDIAN); if (pOutputDevice == DEFAULT) { mOutputLine = AudioSystem.getSourceDataLine(mOutputFormat); } else { System.out.println("+ OUTPUT DEVICE: " + AudioSystem.getMixerInfo()[pOutputDevice]); mOutputLine = AudioSystem.getSourceDataLine(mOutputFormat, - AudioSystem.getMixerInfo()[pOutputDevice]); + AudioSystem.getMixerInfo()[pOutputDevice]); } final int BYTES_PER_SAMPLE = BITS_PER_SAMPLE / 8; mOutputByteBuffer = new byte[mSampleBufferSize * BYTES_PER_SAMPLE * pNumOutputChannels]; @@ -75,15 +78,15 @@ public AudioBufferManager(AudioBufferRenderer pSampleRenderer, /* input */ if (mNumInputChannels > 0) { final AudioFormat mInputFormat = new AudioFormat(pSampleRate, - BITS_PER_SAMPLE, - mNumInputChannels, - SIGNED, - LITTLE_ENDIAN); + BITS_PER_SAMPLE, + mNumInputChannels, + SIGNED, + LITTLE_ENDIAN); if (pInputDevice == DEFAULT) { mInputLine = AudioSystem.getTargetDataLine(mInputFormat); } else { mInputLine = AudioSystem.getTargetDataLine(mInputFormat, - AudioSystem.getMixerInfo()[pInputDevice]); + AudioSystem.getMixerInfo()[pInputDevice]); System.out.println("+ INPUT DEVICE: " + AudioSystem.getMixerInfo()[pInputDevice]); } mInputByteBuffer = new byte[mSampleBufferSize * BYTES_PER_SAMPLE * mNumInputChannels]; @@ -92,7 +95,9 @@ public AudioBufferManager(AudioBufferRenderer pSampleRenderer, } catch (LineUnavailableException e) { System.err.println(e.getMessage()); } - if (mInputLine != null) { mInputLine.start(); } + if (mInputLine != null) { + mInputLine.start(); + } mOutputLine.start(); start(); } diff --git a/src/de/hfkbremen/ton/DSP.java b/src/de/hfkbremen/ton/DSP.java index 09518ed5..48c641e8 100644 --- a/src/de/hfkbremen/ton/DSP.java +++ b/src/de/hfkbremen/ton/DSP.java @@ -1,6 +1,7 @@ package de.hfkbremen.ton; import processing.core.PApplet; +import processing.core.PGraphics; import javax.sound.sampled.AudioSystem; import java.lang.reflect.InvocationTargetException; @@ -10,6 +11,8 @@ public class DSP implements AudioBufferRenderer { + public static final int DEFAULT_SAMPLING_RATE = 44100; + public static final int DEFAULT_AUDIOBLOCK_SIZE = 512; private static final String METHOD_NAME = "audioblock"; private static AudioBufferManager mAudioPlayer; private static DSP mInstance = null; @@ -27,28 +30,52 @@ public DSP(PApplet pPApplet, int pNumberOutputChannels, int pNumberInputChannels try { if (mNumberOutputChannels == 2 && mNumberInputChannels == 2) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class, - float[].class, - float[].class, - float[].class); + float[].class, + float[].class, + float[].class, + float[].class); } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 0) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class, - float[].class); + float[].class, + float[].class); } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 1) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class, - float[].class); + float[].class, + float[].class); } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 0) { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[].class); + float[].class); } else { mMethod = pPApplet.getClass().getDeclaredMethod(METHOD_NAME, - float[][].class); + float[][].class); } } catch (NoSuchMethodException | SecurityException ex) { System.err.println("+++ @DSP / could not find callback `" + METHOD_NAME + "()`. hint: check the callback " + - "method paramters, they must match the sum of input and ouput channels. default is `" + METHOD_NAME + "(float[])` ( = MONO OUTPUT )."); + "method paramters, they must match the sum of input and ouput channels. default is `" + METHOD_NAME + "(float[])` ( = MONO OUTPUT )."); + } + } + + public void audioblock(float[][] pOutputSamples, float[][] pInputSamples) { + try { + if (mNumberOutputChannels == 1 && mNumberInputChannels == 0) { + mMethod.invoke(mPApplet, pOutputSamples[0]); + mCurrentBufferLeft = pOutputSamples[0]; + } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 1) { + mMethod.invoke(mPApplet, pOutputSamples[0], pInputSamples[0]); + mCurrentBufferLeft = pOutputSamples[0]; + } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 0) { + mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1]); + mCurrentBufferLeft = pOutputSamples[0]; + mCurrentBufferRight = pOutputSamples[1]; + } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 2) { + mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1], pInputSamples[0], pInputSamples[1]); + mCurrentBufferLeft = pOutputSamples[0]; + mCurrentBufferRight = pOutputSamples[1]; + } else { + mMethod.invoke(mPApplet, pOutputSamples); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + ex.printStackTrace(); } } @@ -65,19 +92,19 @@ public static DSP start(PApplet pPApplet, int pNumberOutputChannels, int pNumber } public static DSP start(PApplet pPApplet, - int pOutputDevice, - int pNumberOutputChannels, - int pInputDevice, - int pNumberInputChannels) { + int pOutputDevice, + int pNumberOutputChannels, + int pInputDevice, + int pNumberInputChannels) { if (mInstance == null) { mInstance = new DSP(pPApplet, pNumberOutputChannels, pNumberInputChannels); mAudioPlayer = new AudioBufferManager(mInstance, - 44100, - 512, - pOutputDevice, - pNumberOutputChannels, - pInputDevice, - pNumberInputChannels); + DEFAULT_SAMPLING_RATE, + DEFAULT_AUDIOBLOCK_SIZE, + pOutputDevice, + pNumberOutputChannels, + pInputDevice, + pNumberInputChannels); } return mInstance; } @@ -134,27 +161,19 @@ public static float flip(float pValue) { } } - public void audioblock(float[][] pOutputSamples, float[][] pInputSamples) { - try { - if (mNumberOutputChannels == 1 && mNumberInputChannels == 0) { - mMethod.invoke(mPApplet, pOutputSamples[0]); - mCurrentBufferLeft = pOutputSamples[0]; - } else if (mNumberOutputChannels == 1 && mNumberInputChannels == 1) { - mMethod.invoke(mPApplet, pOutputSamples[0], pInputSamples[0]); - mCurrentBufferLeft = pOutputSamples[0]; - } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 0) { - mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1]); - mCurrentBufferLeft = pOutputSamples[0]; - mCurrentBufferRight = pOutputSamples[1]; - } else if (mNumberOutputChannels == 2 && mNumberInputChannels == 2) { - mMethod.invoke(mPApplet, pOutputSamples[0], pOutputSamples[1], pInputSamples[0], pInputSamples[1]); - mCurrentBufferLeft = pOutputSamples[0]; - mCurrentBufferRight = pOutputSamples[1]; - } else { - mMethod.invoke(mPApplet, pOutputSamples); + public static void draw_buffer(PGraphics g, int pWidth, int pHeight) { + final int mBufferSize = DSP.buffer_size(); + if (DSP.buffer() != null) { +// g.beginShape(PConstants.LINES); + for (int i = 0; i < mBufferSize - 1; i++) { +// final float x = PApplet.map(i, 0, mBufferSize, 0, pWidth); +// g.vertex(x, PApplet.map(DSP.buffer()[i], -1, 1, 0, pHeight)); + g.line(PApplet.map(i, 0, mBufferSize, 0, pWidth), + PApplet.map(DSP.buffer()[i], -1, 1, 0, pHeight), + PApplet.map(i + 1, 0, mBufferSize, 0, pWidth), + PApplet.map(DSP.buffer()[i + 1], -1, 1, 0, pHeight)); } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - ex.printStackTrace(); +// g.endShape(); } } } diff --git a/src/de/hfkbremen/ton/ToneEngineJSyn.java b/src/de/hfkbremen/ton/ToneEngineJSyn.java index 953185f2..4858d497 100755 --- a/src/de/hfkbremen/ton/ToneEngineJSyn.java +++ b/src/de/hfkbremen/ton/ToneEngineJSyn.java @@ -9,6 +9,7 @@ import java.util.Timer; import java.util.TimerTask; +import static de.hfkbremen.ton.DSP.DEFAULT_SAMPLING_RATE; import static de.hfkbremen.ton.Note.note_to_frequency; import static de.hfkbremen.ton.Ton.clamp127; import static processing.core.PApplet.constrain; @@ -29,11 +30,11 @@ public ToneEngineJSyn() { } public ToneEngineJSyn(int pDefaultInstrumentType) { - this(pDefaultInstrumentType, 44100, DEFAULT_DEVICE, 1, DEFAULT_DEVICE, 2); + this(pDefaultInstrumentType, DEFAULT_SAMPLING_RATE, DEFAULT_DEVICE, 1, DEFAULT_DEVICE, 2); } public ToneEngineJSyn(int pDefaultInstrumentType, int pOutputDeviceID, int pOutputChannels) { - this(pDefaultInstrumentType, 44100, DEFAULT_DEVICE, 1, pOutputDeviceID, pOutputChannels); + this(pDefaultInstrumentType, DEFAULT_SAMPLING_RATE, DEFAULT_DEVICE, 1, pOutputDeviceID, pOutputChannels); } public ToneEngineJSyn(int pDefaultInstrumentType, diff --git a/src/de/hfkbremen/ton/Wavetable.java b/src/de/hfkbremen/ton/Wavetable.java new file mode 100644 index 00000000..02e21f5b --- /dev/null +++ b/src/de/hfkbremen/ton/Wavetable.java @@ -0,0 +1,79 @@ +package de.hfkbremen.ton; + +import processing.core.PApplet; + +public class Wavetable { + + private final int mSamplingRate; + private final float[] mWavetable; + private float mFrequency; + private float mStepSize; + private float mArrayPtr; + private float mAmplitude; + + public Wavetable(int pWavetableSize) { + this(pWavetableSize, DSP.DEFAULT_SAMPLING_RATE); + } + + public Wavetable(int pWavetableSize, int pSamplingRate) { + mWavetable = new float[pWavetableSize]; + mSamplingRate = pSamplingRate; + mArrayPtr = 0; + mAmplitude = 0.75f; + set_frequency(220); + } + + public void set_frequency(float pFrequency) { + if (mFrequency != pFrequency) { + mFrequency = pFrequency; + mStepSize = mFrequency * ((float) mWavetable.length / (float) mSamplingRate); + } + } + + public void set_amplitude(float pAmplitude) { + mAmplitude = pAmplitude; + } + + public float[] wavetable() { + return mWavetable; + } + + public float process() { + mArrayPtr += mStepSize; + final int i = (int) mArrayPtr; + final float mFrac = mArrayPtr - i; + final int j = i % mWavetable.length; + mArrayPtr = j + mFrac; + return mWavetable[j] * mAmplitude; + } + + public static void sine(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = PApplet.sin(2.0f * PApplet.PI * ((float) i / (float) (pWavetable.length))); + } + } + + public static void sawtooth(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = 2.0f * ((float) i / (float) (pWavetable.length - 1)) - 1.0f; + } + } + + public static void triangle(float[] pWavetable) { + final int q = pWavetable.length / 4; + final float qf = pWavetable.length * 0.25f; + for (int i = 0; i < q; i++) { + pWavetable[i] = i / qf; + pWavetable[i + (q * 1)] = (qf - i) / qf; + pWavetable[i + (q * 2)] = -i / qf; + pWavetable[i + (q * 3)] = -(qf - i) / qf; + } + } + + public static void square(float[] pWavetable) { + for (int i = 0; i < pWavetable.length / 2; i++) { + pWavetable[i] = 1.0f; + pWavetable[i + pWavetable.length / 2] = -1.0f; + } + } +} \ No newline at end of file diff --git a/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java b/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java index dd82e9aa..8150e9cd 100644 --- a/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java +++ b/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP04LowPassFilter.java @@ -63,7 +63,7 @@ public void audioblock(float[] pOutputSamples) { mSample = mButterworthLowPassFilter.filter(mSample); } mSample = DSP.clamp(mSample, -1, 1); - pOutputSamples[i] = mLPFilter.filter(mSample); + pOutputSamples[i] = mSample; } } @@ -75,13 +75,11 @@ private interface filter_constants { /** - * Second order Butterworth low-pass filter - * Dimitris Tassopoulos 2016 + * Second order Butterworth low-pass filter Dimitris Tassopoulos 2016 *
- * fc, corner frequency - * Butterworth low-pass and high-pass filters are specialized versions of the ordinary secondorder - * low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can - * assume before peaking in the frequency response is observed. + * fc, corner frequency Butterworth low-pass and high-pass filters are specialized versions of the ordinary + * secondorder low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can assume before + * peaking in the frequency response is observed. *
* from https://github.com/dimtass/DSP-Cpp-filters */ @@ -91,7 +89,7 @@ private static class ButterworthLowPassFilter { tp_coeffs m_coeffs = new tp_coeffs(); public tp_coeffs calculate_coeffs(int fc) { - final int fs = 44100; + final int fs = DSP.DEFAULT_SAMPLING_RATE; float c = 1.0f / (tan(filter_constants.pi * fc / fs)); m_coeffs.a0 = 1.0f / (1.0f + filter_constants.sqrt2 * c + pow(c, 2.0f)); m_coeffs.a1 = 2.0f * m_coeffs.a0; @@ -103,7 +101,8 @@ public tp_coeffs calculate_coeffs(int fc) { public float filter(float sample) { float xn = sample; - float yn = m_coeffs.a0 * xn + m_coeffs.a1 * m_xnz1 + m_coeffs.a2 * m_xnz2 - m_coeffs.b1 * m_ynz1 - m_coeffs.b2 * m_xnz2; + float yn = + m_coeffs.a0 * xn + m_coeffs.a1 * m_xnz1 + m_coeffs.a2 * m_xnz2 - m_coeffs.b1 * m_ynz1 - m_coeffs.b2 * m_xnz2; m_xnz2 = m_xnz1; m_xnz1 = xn; @@ -116,11 +115,9 @@ public float filter(float sample) { } /** - * Second order Low-pass filter - * Dimitris Tassopoulos 2016 + * Second order Low-pass filter Dimitris Tassopoulos 2016 *
- * fc , corner frequency - * Q , quality factor controlling resonant peaking + * fc , corner frequency Q , quality factor controlling resonant peaking *
* from https://github.com/dimtass/DSP-Cpp-filters */ @@ -130,7 +127,7 @@ private static class SecondOrderLowPassFilter { tp_coeffs m_coeffs = new tp_coeffs(); public tp_coeffs calculate_coeffs(float Q, int fc) { - final int fs = 44100; + final int fs = DSP.DEFAULT_SAMPLING_RATE; float w = 2.0f * filter_constants.pi * fc / fs; float d = 1.0f / Q; float b = 0.5f * (1.0f - (d / 2) * sin(w)) / (1.0f + (d / 2.0f) * sin(w)); diff --git a/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP05Wavetable.java b/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP05Wavetable.java new file mode 100644 index 00000000..cb6661a1 --- /dev/null +++ b/src/de/hfkbremen/ton/examples_ext/SketchExampleDSP05Wavetable.java @@ -0,0 +1,135 @@ +package de.hfkbremen.ton.examples_ext; + +import de.hfkbremen.ton.DSP; +import processing.core.PApplet; + +public class SketchExampleDSP05Wavetable extends PApplet { + + private final Wavetable mWavetable = new Wavetable(1024); + + public void settings() { + size(640, 480); + } + + public void setup() { + DSP.dumpAudioDevices(); + DSP.start(this); + triangle(mWavetable.wavetable()); + } + + public void draw() { + background(255); + DSP.draw_buffer(g, width, height); + } + + public void mouseMoved() { + mWavetable.set_frequency(map(mouseX, 0, width, 55, 220)); + mWavetable.set_amplitude(map(mouseY, 0, height, 0.0f, 0.9f)); + } + + public void keyPressed() { + switch (key) { + case '1': + sine(mWavetable.wavetable()); + break; + case '2': + sawtooth(mWavetable.wavetable()); + break; + case '3': + triangle(mWavetable.wavetable()); + break; + case '4': + square(mWavetable.wavetable()); + break; + case '5': + randomize(mWavetable.wavetable()); + break; + } + } + + public void audioblock(float[] pOutputSamples) { + for (int i = 0; i < pOutputSamples.length; i++) { + pOutputSamples[i] = mWavetable.process(); + } + } + + private void randomize(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = random(-1, 1); + } + } + + public static void sine(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = PApplet.sin(2.0f * PI * ((float) i / (float) (pWavetable.length))); + } + } + + public static void sawtooth(float[] pWavetable) { + for (int i = 0; i < pWavetable.length; i++) { + pWavetable[i] = 2.0f * ((float) i / (float) (pWavetable.length - 1)) - 1.0f; + } + } + + public static void triangle(float[] pWavetable) { + final int q = pWavetable.length / 4; + final float qf = pWavetable.length * 0.25f; + for (int i = 0; i < q; i++) { + pWavetable[i] = i / qf; + pWavetable[i + (q * 1)] = (qf - i) / qf; + pWavetable[i + (q * 2)] = -i / qf; + pWavetable[i + (q * 3)] = -(qf - i) / qf; + } + } + + public static void square(float[] pWavetable) { + for (int i = 0; i < pWavetable.length / 2; i++) { + pWavetable[i] = 1.0f; + pWavetable[i + pWavetable.length / 2] = -1.0f; + } + } + + private static class Wavetable { + + private final float[] mWavetable; + private float mFrequency; + private float mStepSize; + private float mArrayPtr; + private float mAmplitude; + + public Wavetable(int pWavetableSize) { + mWavetable = new float[pWavetableSize]; + mArrayPtr = 0; + mAmplitude = 0.75f; + set_frequency(220); + } + + public void set_frequency(float pFrequency) { + if (mFrequency != pFrequency) { + mFrequency = pFrequency; + mStepSize = mFrequency * ((float) mWavetable.length / (float) DSP.DEFAULT_SAMPLING_RATE); + } + } + + public void set_amplitude(float pAmplitude) { + mAmplitude = pAmplitude; + } + + public float[] wavetable() { + return mWavetable; + } + + public float process() { + mArrayPtr += mStepSize; + final int i = (int) mArrayPtr; + final float mFrac = mArrayPtr - i; + final int j = i % mWavetable.length; + mArrayPtr = j + mFrac; + return mWavetable[j] * mAmplitude; + } + } + + public static void main(String[] args) { + PApplet.main(SketchExampleDSP05Wavetable.class.getName()); + } +} \ No newline at end of file diff --git a/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java b/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java index 728c001f..73467763 100755 --- a/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java +++ b/src/de/hfkbremen/ton/examples_ext/SketchExampleInstruments01ADSR.java @@ -26,24 +26,24 @@ */ public class SketchExampleInstruments01ADSR extends PApplet { - private Parameter mParameterAttack; - private Parameter mParameterDecay; - private Parameter mParameterSustain; - private Parameter mParameterRelease; + private Slider mSliderAttack; + private Slider mSliderDecay; + private Slider mSliderSustain; + private Slider mSliderRelease; private int mNote; public void settings() { - size(640, 480, P2D); + size(640, 480); } public void setup() { hint(DISABLE_KEY_REPEAT); - mParameterAttack = new Parameter(); - mParameterDecay = new Parameter(); - mParameterSustain = new Parameter(); - mParameterSustain.horziontal = false; - mParameterRelease = new Parameter(); + mSliderAttack = new Slider(); + mSliderDecay = new Slider(); + mSliderSustain = new Slider(); + mSliderSustain.horziontal = false; + mSliderRelease = new Slider(); updateADSR(); println(Instrument.ADSR_DIAGRAM); } @@ -55,7 +55,7 @@ public void draw() { } else { background(255); } - final float mXOffset = (width - Parameter.size * 4) * 0.5f; + final float mXOffset = (width - Slider.size * 4) * 0.5f; final float mYOffset = height * 0.5f; translate(mXOffset, mYOffset); scale(1, -1); @@ -78,68 +78,72 @@ public void keyReleased() { } private void drawDiagram() { - stroke(0, 63); - line(0, 0, Parameter.size * 4, 0); - - stroke(0); - line(0, 0, - mParameterAttack.current_position_x(), - mParameterAttack.current_position_y()); - line(mParameterAttack.current_position_x(), - mParameterAttack.current_position_y(), - mParameterDecay.current_position_x(), - mParameterDecay.current_position_y()); - line(mParameterDecay.current_position_x(), - mParameterDecay.current_position_y(), - mParameterSustain.current_position_x(), - mParameterSustain.current_position_y()); - line(mParameterSustain.current_position_x(), - mParameterSustain.current_position_y(), - mParameterRelease.current_position_x(), - mParameterRelease.current_position_y()); + stroke(0, 15); + line(0, 0, Slider.size * 4, 0); + /* GUI */ noStroke(); fill(0); - ellipse(0, 0, Parameter.radius * 2, Parameter.radius * 2); + ellipse(0, 0, Slider.radius, Slider.radius); + + mSliderAttack.draw(g); + mSliderDecay.draw(g); + mSliderSustain.draw(g); + mSliderRelease.draw(g); - mParameterAttack.draw(g); - mParameterDecay.draw(g); - mParameterSustain.draw(g); - mParameterRelease.draw(g); + /* envelope */ + strokeWeight(3); + stroke(0); + line(0, 0, + mSliderAttack.current_position_x(), + mSliderAttack.current_position_y()); + line(mSliderAttack.current_position_x(), + mSliderAttack.current_position_y(), + mSliderDecay.current_position_x(), + mSliderDecay.current_position_y()); + line(mSliderDecay.current_position_x(), + mSliderDecay.current_position_y(), + mSliderSustain.current_position_x(), + mSliderSustain.current_position_y()); + line(mSliderSustain.current_position_x(), + mSliderSustain.current_position_y(), + mSliderRelease.current_position_x(), + mSliderRelease.current_position_y()); + strokeWeight(1); } private void updateDiagram(float mXOffset, float mYOffset) { float mX = mouseX - mXOffset; float mY = -mouseY + mYOffset; - mParameterAttack.update(mX, mY, mousePressed); - mParameterAttack.x = 0; - mParameterAttack.y = Parameter.size; + mSliderAttack.update(mX, mY, mousePressed); + mSliderAttack.x = 0; + mSliderAttack.y = Slider.size; - mParameterDecay.update(mX, mY, mousePressed); - mParameterDecay.x = mParameterAttack.current_position_x(); - mParameterDecay.y = mParameterSustain.current_position_y(); + mSliderDecay.update(mX, mY, mousePressed); + mSliderDecay.x = mSliderAttack.current_position_x(); + mSliderDecay.y = mSliderSustain.current_position_y(); - mParameterSustain.update(mX, mY, mousePressed); - mParameterSustain.x = mParameterDecay.current_position_x() + Parameter.size; - mParameterSustain.y = 0; + mSliderSustain.update(mX, mY, mousePressed); + mSliderSustain.x = mSliderDecay.current_position_x() + Slider.size; + mSliderSustain.y = 0; - mParameterRelease.update(mX, mY, mousePressed); - mParameterRelease.x = mParameterSustain.x; - mParameterRelease.y = 0; + mSliderRelease.update(mX, mY, mousePressed); + mSliderRelease.x = mSliderSustain.x; + mSliderRelease.y = 0; } private void updateADSR() { - Ton.instrument().attack(mParameterAttack.value); - Ton.instrument().decay(mParameterDecay.value); - Ton.instrument().sustain(mParameterSustain.value); - Ton.instrument().release(mParameterRelease.value); + Ton.instrument().attack(mSliderAttack.value); + Ton.instrument().decay(mSliderDecay.value); + Ton.instrument().sustain(mSliderSustain.value); + Ton.instrument().release(mSliderRelease.value); } - private static class Parameter { + private static class Slider { private static final float size = 120; - private static final float radius = 6; + private static final float radius = 8; float x; float y; float value; @@ -147,7 +151,7 @@ private static class Parameter { boolean hoover; boolean drag; - public Parameter() { + public Slider() { x = 0; y = 0; value = 0.5f; @@ -157,12 +161,18 @@ public Parameter() { } void draw(PGraphics g) { -// g.stroke(0, 63); -// g.line(x, y, x + (horziontal ? size : 0), y + (horziontal ? 0 : size)); + g.stroke(191); + g.line(x, y, x + (horziontal ? size : 0), y + (horziontal ? 0 : size)); + + final float mEdgeDiameter = 5; + g.noStroke(); + g.fill(0); + g.ellipse(x, y, mEdgeDiameter, mEdgeDiameter); + g.ellipse(x + (horziontal ? size : 0), y + (horziontal ? 0 : size), mEdgeDiameter, mEdgeDiameter); g.noStroke(); - g.fill(hoover ? (drag ? 223 : 127) : 0); - g.ellipse(current_position_x(), current_position_y(), radius * 2, radius * 2); + g.fill(0); + g.ellipse(current_position_x(), current_position_y(), radius * (hoover ? 2 : 1), radius * (hoover ? 2 : 1)); } void update_value(float pX, float pY) { @@ -172,7 +182,7 @@ void update_value(float pX, float pY) { boolean hit(float pX, float pY) { final float mDistance = PVector.dist(new PVector().set(pX, pY), - new PVector().set(current_position_x(), current_position_y())); + new PVector().set(current_position_x(), current_position_y())); return mDistance < radius * 2; } diff --git a/ton.zip b/ton.zip index a2b48242..8343ce9f 100644 Binary files a/ton.zip and b/ton.zip differ