diff --git a/java/libraries/sound/.gitignore b/java/libraries/sound/.gitignore new file mode 100644 index 000000000..06a0ef56d --- /dev/null +++ b/java/libraries/sound/.gitignore @@ -0,0 +1,5 @@ +*.o +*.class +.DS_Store +/distribution +/bin diff --git a/java/libraries/sound/build.xml b/java/libraries/sound/build.xml new file mode 100755 index 000000000..7f403f259 --- /dev/null +++ b/java/libraries/sound/build.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/libraries/sound/examples/Analysis/AmplitudeRMS/AmplitudeRMS.pde b/java/libraries/sound/examples/Analysis/AmplitudeRMS/AmplitudeRMS.pde new file mode 100644 index 000000000..f13746792 --- /dev/null +++ b/java/libraries/sound/examples/Analysis/AmplitudeRMS/AmplitudeRMS.pde @@ -0,0 +1,41 @@ +/* +This example shows how to use the RMS amplitude tracker. The tracker +calculates the Root Mean Square over a block of audio and returns +the mean as a float between 0 and 1. +*/ + +import processing.sound.*; + +Sound stream; +SoundFile sample; +Amplitude rms; + +int scale=1; + +public void setup() { + size(350,350); + + // Create and start the sound renderer + stream = new Sound(this, 44100, 512); + + //Load and play a soundfile and loop it + sample = new SoundFile(this, "beat.aiff"); + sample.loop(); + + // Create and patch the rms tracker + rms = new Amplitude(this); + rms.input(sample); +} + +public void draw() { + background(125,255,125); + + // rms.process() return a value between 0 and 1. To adjust + // the scaling and mapping of an ellipse we scale from 0 to 0.5 + scale=int(map(rms.process(), 0, 0.5, 1, 350)); + noStroke(); + + fill(255,0,150); + // We draw an ellispe coupled to the audio analysis + ellipse(width/2, height/2, 1*scale, 1*scale); +} diff --git a/java/libraries/sound/examples/Analysis/AmplitudeRMS/data/beat.aiff b/java/libraries/sound/examples/Analysis/AmplitudeRMS/data/beat.aiff new file mode 100644 index 000000000..017b7ce23 Binary files /dev/null and b/java/libraries/sound/examples/Analysis/AmplitudeRMS/data/beat.aiff differ diff --git a/java/libraries/sound/examples/Analysis/FFTSpectrum/FFTSpectrum.pde b/java/libraries/sound/examples/Analysis/FFTSpectrum/FFTSpectrum.pde new file mode 100644 index 000000000..7bc9af57e --- /dev/null +++ b/java/libraries/sound/examples/Analysis/FFTSpectrum/FFTSpectrum.pde @@ -0,0 +1,47 @@ +/* +This example shows how to use the Fast Fourier Transform function to get the spectrum +of a sound. This function calculates the FFT of a signal and returns the positive normalized +magnitude spectrum. This means we pass it the number of bands we want (the actual FFT size is +two times that size) and a float array with the same size. +*/ + +import processing.sound.*; + +Sound stream; +SoundFile sample; +FFT fft; + +int scale=1; +int bands=512; +float[] spec = new float[bands]; + +public void setup() { + size(bands,350); + background(255); + + // Create and start the sound renderer + stream = new Sound(this, 44100, 256); + + //Load and play a soundfile and loop it. This has to be called + // before the FFT is created. + sample = new SoundFile(this, "beat.aiff"); + sample.loop(); + + // Create and patch the rms tracker + fft = new FFT(this); + fft.input(sample, bands); +} + +public void draw() { + background(255); + + fft.process(spec); + + for(int i = 0; i < bands; i++) + { + // The result of the FFT is normalized + // draw the line for frequency band i scaling it up by 5 to get more amplitude. + line( i, height, i, height - spec[i]*height*5 ); + } +} + diff --git a/java/libraries/sound/examples/Analysis/FFTSpectrum/data/beat.aiff b/java/libraries/sound/examples/Analysis/FFTSpectrum/data/beat.aiff new file mode 100644 index 000000000..017b7ce23 Binary files /dev/null and b/java/libraries/sound/examples/Analysis/FFTSpectrum/data/beat.aiff differ diff --git a/java/libraries/sound/examples/Effects/Filter/BandPass/BandPass.pde b/java/libraries/sound/examples/Effects/Filter/BandPass/BandPass.pde new file mode 100644 index 000000000..e7f9e422a --- /dev/null +++ b/java/libraries/sound/examples/Effects/Filter/BandPass/BandPass.pde @@ -0,0 +1,31 @@ +/* +This is a simple WhiteNoise generator. It can be started with .play(float amp). +In this example it is started and stopped by clicking into the renderer window. +*/ + +import processing.sound.*; + +Sound stream; +WhiteNoise noise; +BPF bandPass; + +float amp=0.0; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the noise generator + stream = new Sound(this); + noise = new WhiteNoise(this); + bandPass = new BPF(this); + noise.play(0.5); + bandPass.play(noise, 100); +} + +void draw() { + + bandPass.freq(map(mouseX, 0, 350, 20, 10000)); + + bandPass.res(map(mouseY, 0, 350, 0.05, 1.0)); +} diff --git a/java/libraries/sound/examples/Effects/Filter/HighPass/HighPass.pde b/java/libraries/sound/examples/Effects/Filter/HighPass/HighPass.pde new file mode 100644 index 000000000..546a86dd5 --- /dev/null +++ b/java/libraries/sound/examples/Effects/Filter/HighPass/HighPass.pde @@ -0,0 +1,29 @@ +/* +This is a simple WhiteNoise generator. It can be started with .play(float amp). +In this example it is started and stopped by clicking into the renderer window. +*/ + +import processing.sound.*; + +Sound stream; +WhiteNoise noise; +HPF highPass; + +float amp=0.0; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the noise generator + stream = new Sound(this); + noise = new WhiteNoise(this); + highPass = new HPF(this); + noise.play(0.5); + highPass.play(noise, 100); +} + +void draw() { + + highPass.freq(map(mouseX, 0, 350, 20, 10000)); +} diff --git a/java/libraries/sound/examples/Effects/Filter/LowPass/LowPass.pde b/java/libraries/sound/examples/Effects/Filter/LowPass/LowPass.pde new file mode 100644 index 000000000..663b16b9e --- /dev/null +++ b/java/libraries/sound/examples/Effects/Filter/LowPass/LowPass.pde @@ -0,0 +1,30 @@ +/* +This is a simple WhiteNoise generator. It can be started with .play(float amp). +In this example it is started and stopped by clicking into the renderer window. +*/ + +import processing.sound.*; + +Sound stream; +WhiteNoise noise; +LPF lowPass; + + +float amp=0.0; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the noise generator + stream = new Sound(this); + noise = new WhiteNoise(this); + lowPass = new LPF(this); + noise.play(0.5); + lowPass.play(noise, 100); +} + +void draw() { + + lowPass.freq(map(mouseX, 0, 350, 20, 10000)); +} diff --git a/java/libraries/sound/examples/Effects/Variable_Delay/Variable_Delay.pde b/java/libraries/sound/examples/Effects/Variable_Delay/Variable_Delay.pde new file mode 100644 index 000000000..52ae04f8f --- /dev/null +++ b/java/libraries/sound/examples/Effects/Variable_Delay/Variable_Delay.pde @@ -0,0 +1,53 @@ +/* +This is a sound file player. +*/ + +import processing.sound.*; + +Sound stream; +SoundFile soundfile; +Delay delay; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer + stream = new Sound(this); + + //Load a soundfile + soundfile = new SoundFile(this, "vibraphon.aiff"); + + // create a Delay Effect + delay = new Delay(this); + + // These methods return useful infos about the file + println("SFSampleRate= " + soundfile.sampleRate() + " Hz"); + println("SFSamples= " + soundfile.frames() + " samples"); + println("SFDuration= " + soundfile.duration() + " seconds"); + + // Play the file in a loop + soundfile.loop(); + + // Patch the delay + delay.play(soundfile, 5); +} + + +void draw() { + // Map mouseX from 0.25 to 4.0 for playback rate. 1 equals original playback + // speed 2 is an octave up 0.5 is an octave down. + soundfile.rate(map(mouseX, 0, 350, 0.25, 4.0)); + + // Map mouseY from 0.2 to 1.0 for amplitude + soundfile.amp(map(mouseY, 0, 350, 0.2, 1.0)); + + // Map mouseY from -1.0 to 1.0 for left to right + soundfile.pan(map(mouseY, 0, 350, -1.0, 1.0)); + + // Map mouseY from 0.001 to 2.0 seconds for the delaytime + delay.time(map(mouseY, 0, 350, 0.001, 2.0)); + + // Map mouseX from 0 to 0.8 for the delay feedback + delay.feedback(map(mouseX, 0, 350, 0.0, 0.8)); +} diff --git a/java/libraries/sound/examples/Effects/Variable_Delay/data/vibraphon.aiff b/java/libraries/sound/examples/Effects/Variable_Delay/data/vibraphon.aiff new file mode 100644 index 000000000..b4e95a3a9 Binary files /dev/null and b/java/libraries/sound/examples/Effects/Variable_Delay/data/vibraphon.aiff differ diff --git a/java/libraries/sound/examples/Keyboard/data/1.aif b/java/libraries/sound/examples/Keyboard/data/1.aif new file mode 100644 index 000000000..fd65b3123 Binary files /dev/null and b/java/libraries/sound/examples/Keyboard/data/1.aif differ diff --git a/java/libraries/sound/examples/Keyboard/data/2.aif b/java/libraries/sound/examples/Keyboard/data/2.aif new file mode 100644 index 000000000..742301e7a Binary files /dev/null and b/java/libraries/sound/examples/Keyboard/data/2.aif differ diff --git a/java/libraries/sound/examples/Keyboard/data/3.aif b/java/libraries/sound/examples/Keyboard/data/3.aif new file mode 100644 index 000000000..7b8da50f3 Binary files /dev/null and b/java/libraries/sound/examples/Keyboard/data/3.aif differ diff --git a/java/libraries/sound/examples/Keyboard/data/4.aif b/java/libraries/sound/examples/Keyboard/data/4.aif new file mode 100644 index 000000000..7884a3418 Binary files /dev/null and b/java/libraries/sound/examples/Keyboard/data/4.aif differ diff --git a/java/libraries/sound/examples/Keyboard/data/5.aif b/java/libraries/sound/examples/Keyboard/data/5.aif new file mode 100644 index 000000000..5664dad51 Binary files /dev/null and b/java/libraries/sound/examples/Keyboard/data/5.aif differ diff --git a/java/libraries/sound/examples/Keyboard/keyboard.pde b/java/libraries/sound/examples/Keyboard/keyboard.pde new file mode 100644 index 000000000..73b32f5eb --- /dev/null +++ b/java/libraries/sound/examples/Keyboard/keyboard.pde @@ -0,0 +1,138 @@ +/* +This example shows how to make a simple sampler and sequencer with the Sound library. In this +sketch 5 different short samples are loaded and played back at different pitches, in this +case 5 different octaves. The sequencer triggers and event every 200-1000 mSecs randomly. +Each time a sound is played a colored rect with a random color is displayed. +*/ + +import processing.sound.*; + +Sound stream; +SoundFile[] file; + +// Define the number of samples +int numsounds = 5; + +int value[] = {0,0,0}; + + +void setup(){ + size(500, 500); + background(255); + + // Create a Sound renderer and an array of empty soundfiles + stream = new Sound(this, 44100, 32); + file = new SoundFile[numsounds]; + + // Load 5 soundfiles from a folder in a for loop. By naming the files 1., 2., 3., n.aif it is easy to iterate + // through the folder and load all files in one line of code. + for (int i = 0; i < numsounds; i++){ + file[i] = new SoundFile(this, (i+1) + ".aif"); + } + +} + +void draw(){ + + background(value[0],value[1],value[2]); + +} + + + +void keyPressed() { + for (int i=0; i < 3; i++) { + value[i]=int(random(255)); + }; + + + switch(key){ + case 'a': + file[0].play(0.5, 1.0); + break; + + case 's': + file[1].play(0.5, 1.0); + break; + + case 'd': + file[2].play(0.5, 1.0); + break; + + case 'f': + file[3].play(0.5, 1.0); + break; + + case 'g': + file[4].play(0.5, 1.0); + break; + + case 'h': + file[0].play(1.0, 1.0); + break; + + case 'j': + file[1].play(1.0, 1.0); + break; + + case 'k': + file[2].play(1.0, 1.0); + break; + + case 'l': + file[3].play(1.0, 1.0); + break; + + case 'ö': + file[4].play(1.0, 1.0); + break; + + case 'ä': + file[0].play(2.0, 1.0); + break; + + case 'q': + file[1].play(2.0, 1.0); + break; + + case 'w': + file[2].play(2.0, 1.0); + break; + + case 'e': + file[3].play(2.0, 1.0); + break; + + case 'r': + file[4].play(2.0, 1.0); + break; + + case 't': + file[0].play(3.0, 1.0); + break; + + case 'z': + file[1].play(3.0, 1.0); + break; + + case 'u': + file[2].play(3.0, 1.0); + break; + + case 'i': + file[3].play(3.0, 1.0); + break; + + case 'o': + file[4].play(3.0, 1.0); + break; + + case 'p': + file[0].play(4.0, 1.0); + break; + + case 'ü': + file[1].play(4.0, 1.0); + break; + } +} diff --git a/java/libraries/sound/examples/Oscillators/Noise/Noise.pde b/java/libraries/sound/examples/Oscillators/Noise/Noise.pde new file mode 100644 index 000000000..85fa9dfd2 --- /dev/null +++ b/java/libraries/sound/examples/Oscillators/Noise/Noise.pde @@ -0,0 +1,29 @@ +/* +This is a simple WhiteNoise generator. It can be started with .play(float amp). +In this example it is started and stopped by clicking into the renderer window. +*/ + +import processing.sound.*; + +Sound stream; +WhiteNoise noise; + +float amp=0.0; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the noise generator + stream = new Sound(this); + noise = new WhiteNoise(this); + noise.play(); +} + +void draw() { + // Map mouseX from 0.0 to 1.0 for amplitude + noise.amp(map(mouseX, 0, 350, 0.0, 1.0)); + + // Map mouseY from -1.0 to 1.0 for left to right + noise.pan(map(mouseY, 0, 350, -1.0, 1.0)); +} diff --git a/java/libraries/sound/examples/Oscillators/PulseWidth/PulseWidth.pde b/java/libraries/sound/examples/Oscillators/PulseWidth/PulseWidth.pde new file mode 100644 index 000000000..b009dbb96 --- /dev/null +++ b/java/libraries/sound/examples/Oscillators/PulseWidth/PulseWidth.pde @@ -0,0 +1,33 @@ +/* +This is a pulse-wave oscillator. The method .play() starts the oscillator. +There are several setters like .amp(), .freq(), .width(), .pan() and .add(). +If you want to set all of them at the same time use +.set(float freq, float width, float amp, float add, float pan) +*/ + +import processing.sound.*; + +Sound stream; +Pulse pulse; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the pulse wave oscillator + stream = new Sound(this); + pulse = new Pulse(this); + pulse.play(); +} + +void draw() { + // Map mouseX from 20Hz to 500Hz for frequency + pulse.freq(map(mouseX, 0, width, 20.0, 500.0)); + // Map mouseX from 0.0 to 0.5 for amplitude + pulse.pan(map(mouseX, 0, width, -1.0, 1.0)); + // Map mouseY from 0.0 to 0.5 for amplitude + pulse.amp(map(mouseY, 0, height, 0.0, 0.5)); + // Map mouseY from 0.0 to 0.5 for amplitude + pulse.width(map(mouseY, 0, height, 0.0, 1.0)); +} + diff --git a/java/libraries/sound/examples/Oscillators/SawWave/SawWave.pde b/java/libraries/sound/examples/Oscillators/SawWave/SawWave.pde new file mode 100644 index 000000000..a39377e76 --- /dev/null +++ b/java/libraries/sound/examples/Oscillators/SawWave/SawWave.pde @@ -0,0 +1,36 @@ +/* +This is a saw-wave oscillator. The method .play() starts the oscillator. There +are several setters like .amp(), .freq(), .pan() and .add(). If you want to set all of them at +the same time use .set(float freq, float amp, float add, float pan) +*/ + +import processing.sound.*; + +Sound stream; +SawOsc saw; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the sine oscillator. + // Second and Third arguments are optional, default is 44100 / 512 + // for Sample Rate and Buffer Size + stream = new Sound(this, 44100, 256); + saw = new SawOsc(this); + + //Start the Sine Oscillator. There will be no sound in the beginning + //unless the mouse enters the + saw.play(); +} + +void draw() { + // Map mouseY from 0.0 to 1.0 for amplitude + saw.amp(map(mouseY, 0, height, 1.0, 0.0)); + + // Map mouseX from 20Hz to 1000Hz for frequency + saw.freq(map(mouseX, 0, width, 80.0, 200.0)); + + // Map mouseX from -1.0 to 1.0 for left to right + saw.pan(map(mouseX, 0, width, -1.0, 1.0)); +} diff --git a/java/libraries/sound/examples/Oscillators/SineWave/SineWave.pde b/java/libraries/sound/examples/Oscillators/SineWave/SineWave.pde new file mode 100644 index 000000000..3f1310491 --- /dev/null +++ b/java/libraries/sound/examples/Oscillators/SineWave/SineWave.pde @@ -0,0 +1,43 @@ +/* +This is a sine-wave oscillator. The method .play() starts the oscillator. There +are several setters like .amp(), .freq(), .pan() and .add(). If you want to set all of them at +the same time use .set(float freq, float amp, float add, float pan) +*/ + +import processing.sound.*; + +Sound stream; +SinOsc sine; + +float freq=400; +float amp=0.5; +float pos; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the sine oscillator. + // Second and Third arguments are optional, default is 44100 / 512 + // for Sample Rate and Buffer Size + stream = new Sound(this, 44100, 256); + sine = new SinOsc(this); + + //Start the Sine Oscillator. + sine.play(); +} + +void draw() { + + // Map mouseY from 0.0 to 1.0 for amplitude + amp=map(mouseY, 0, height, 1.0, 0.0); + sine.amp(amp); + + // Map mouseX from 20Hz to 1000Hz for frequency + freq=map(mouseX, 0, width, 80.0, 1000.0); + sine.freq(freq); + + // Map mouseX from -1.0 to 1.0 for left to right + pos=map(mouseX, 0, width, -1.0, 1.0); + sine.pan(pos); +} diff --git a/java/libraries/sound/examples/Oscillators/SqrWave/SqrWave.pde b/java/libraries/sound/examples/Oscillators/SqrWave/SqrWave.pde new file mode 100644 index 000000000..0c4597a6b --- /dev/null +++ b/java/libraries/sound/examples/Oscillators/SqrWave/SqrWave.pde @@ -0,0 +1,36 @@ +/* +This is a saw-wave oscillator. The method .play() starts the oscillator. There +are several setters like .amp(), .freq(), .pan() and .add(). If you want to set all of them at +the same time use .set(float freq, float amp, float add, float pan) +*/ + +import processing.sound.*; + +Sound stream; +SqrOsc sqr; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the sine oscillator. + // Second and Third arguments are optional, default is 44100 / 512 + // for Sample Rate and Buffer Size + stream = new Sound(this, 44100, 256); + sqr = new SqrOsc(this); + + //Start the Sine Oscillator. There will be no sound in the beginning + //unless the mouse enters the + sqr.play(); +} + +void draw() { + // Map mouseY from 0.0 to 1.0 for amplitude + sqr.amp(map(mouseY, 0, height, 1.0, 0.0)); + + // Map mouseX from 20Hz to 1000Hz for frequency + sqr.freq(map(mouseX, 0, width, 80.0, 200.0)); + + // Map mouseX from -1.0 to 1.0 for left to right + sqr.pan(map(mouseX, 0, width, -1.0, 1.0)); +} diff --git a/java/libraries/sound/examples/Oscillators/TriWave/TriWave.pde b/java/libraries/sound/examples/Oscillators/TriWave/TriWave.pde new file mode 100644 index 000000000..ec2ebefac --- /dev/null +++ b/java/libraries/sound/examples/Oscillators/TriWave/TriWave.pde @@ -0,0 +1,36 @@ +/* +This is a saw-wave oscillator. The method .play() starts the oscillator. There +are several setters like .amp(), .freq(), .pan() and .add(). If you want to set all of them at +the same time use .set(float freq, float amp, float add, float pan) +*/ + +import processing.sound.*; + +Sound stream; +TriOsc tri; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer and the sine oscillator. + // Second and Third arguments are optional, default is 44100 / 512 + // for Sample Rate and Buffer Size + stream = new Sound(this, 44100, 256); + tri = new TriOsc(this); + + //Start the Sine Oscillator. There will be no sound in the beginning + //unless the mouse enters the + tri.play(); +} + +void draw() { + // Map mouseY from 0.0 to 1.0 for amplitude + tri.amp(map(mouseY, 0, height, 1.0, 0.0)); + + // Map mouseX from 20Hz to 1000Hz for frequency + tri.freq(map(mouseX, 0, width, 80.0, 1000.0)); + + // Map mouseX from -1.0 to 1.0 for left to right + tri.pan(map(mouseX, 0, width, -1.0, 1.0)); +} diff --git a/java/libraries/sound/examples/Soundfiles/Sample/Sample.pde b/java/libraries/sound/examples/Soundfiles/Sample/Sample.pde new file mode 100644 index 000000000..3f5aa8278 --- /dev/null +++ b/java/libraries/sound/examples/Soundfiles/Sample/Sample.pde @@ -0,0 +1,42 @@ +/* +This is a sound file player. +*/ + + +import processing.sound.*; + +Sound stream; +SoundFile soundfile; + +void setup() { + size(350,350); + background(255); + + // Create and start the sound renderer + stream = new Sound(this); + + //Load a soundfile + soundfile = new SoundFile(this, "vibraphon.aiff"); + + // These methods return useful infos about the file + println("SFSampleRate= " + soundfile.sampleRate() + " Hz"); + println("SFSamples= " + soundfile.frames() + " samples"); + println("SFDuration= " + soundfile.duration() + " seconds"); + + // Play the file in a loop + soundfile.loop(); +} + + +void draw() { + // Map mouseX from 0.25 to 4.0 for playback rate. 1 equals original playback + // speed 2 is an octave up 0.5 is an octave down. + soundfile.rate(map(mouseX, 0, 350, 0.25, 4.0)); + + // Map mouseY from 0.2 to 1.0 for amplitude + soundfile.amp(map(mouseY, 0, 350, 0.2, 1.0)); + + // Map mouseY from -1.0 to 1.0 for left to right + soundfile.pan(map(mouseY, 0, 350, -1.0, 1.0)); +} + diff --git a/java/libraries/sound/examples/Soundfiles/Sample/data/vibraphon.aiff b/java/libraries/sound/examples/Soundfiles/Sample/data/vibraphon.aiff new file mode 100644 index 000000000..b4e95a3a9 Binary files /dev/null and b/java/libraries/sound/examples/Soundfiles/Sample/data/vibraphon.aiff differ diff --git a/java/libraries/sound/examples/processing_handbook/ex1/ex1.pde b/java/libraries/sound/examples/processing_handbook/ex1/ex1.pde new file mode 100644 index 000000000..7d5f701e3 --- /dev/null +++ b/java/libraries/sound/examples/processing_handbook/ex1/ex1.pde @@ -0,0 +1,60 @@ +/* +This example shows how to create a cluster of sine oscillators, change the frequency and detune them +depending on the position of the mouse in the renderer window. The Y position determines the basic +frequency of the oscillator and X the detuning of the oscillator. The basic frequncy ranges between +150 and 1150 Hz. +*/ + +import processing.sound.*; + +Sound stream; +SinOsc[] sineWaves; + +// The number of oscillators +int numSines = 5; + +// A float for calculating the amplitudes +float[] sineVolume; + +void setup() { + size(500, 500); + background(255); + + //Create and start the Sound renderer + stream = new Sound(this); + + // Create the oscillators and amplitudes + sineWaves = new SinOsc[numSines]; + sineVolume = new float[numSines]; + + for (int i = 0; i < numSines; i++) { + + // The overall amplitude shouldn't exceed 1.0 which is prevented by 1.0/numSines. + // The ascending waves will get lower in volume the higher the frequency + sineVolume[i] = (1.0 / numSines) / (i + 1); + + // Create the Sine Oscillators and start them + sineWaves[i] = new SinOsc(this); + sineWaves[i].play(); + } +} + +void draw() { + noStroke(); + + // Map mouseY to get values from 0.0 to 1.0 + float yoffset = (height - mouseY) / float(height); + + // Map that value logarithmically to 150 - 1150 Hz + float frequency = pow(1000, yoffset) + 150; + + // Map mouseX from -0.5 to 0.5 to get a multiplier for detuning the oscillators + float detune = float(mouseX) / width - 0.5; + + // Set the frequencies, detuning and volume + for (int i = 0; i < numSines; i++) { + sineWaves[i].freq(frequency * (i + 1 + i * detune)); + sineWaves[i].amp(sineVolume[i]); + + } +} diff --git a/java/libraries/sound/examples/processing_handbook/ex2/ex2.pde b/java/libraries/sound/examples/processing_handbook/ex2/ex2.pde new file mode 100644 index 000000000..72f212f2d --- /dev/null +++ b/java/libraries/sound/examples/processing_handbook/ex2/ex2.pde @@ -0,0 +1,79 @@ +/* +This sketch shows how to use envelopes and oscillators. Envelopes are pre-defined amplitude +distribution over time. The sound library provides an ASR envelope which stands for attach, +sustain, release. The amplitude rises then sustains at the maximum level and decays slowly +depending on pre defined time segments. + + .________ + . --- + . --- + . --- + A S R + +*/ + +import processing.sound.*; + +Sound stream; +TriOsc triOsc; +Env env; + +// Times and levels for the ASR envelope +float attackTime = 0.001; +float sustainTime = 0.004; +float sustainLevel = 0.3; +float releaseTime = 0.4; + +// This is an octave in MIDI notes. +int[] midiSequence = { 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72}; +int duration = 200; +// Set the note trigger +int trigger = millis(); + +// An index to count up the notes +int note=0; + +void setup() { + size(200, 200); + background(255); + + //Create and start the Sound renderer + stream = new Sound(this); + + // Create triangle wave and start it + triOsc = new TriOsc(this); + //triOsc.play(); + + // Create the envelope + env = new Env(this); + +} + +void draw() { + + // If the determined trigger moment in time matches up with the computer clock and we if the + // sequence of notes hasn't been finished yet the next note gets played. + if ((millis() > trigger) && (note trigger){ + // Redraw the background every time to erase old rects + background(255); + + // By iterating through the playSound array we check for 1 or 0, 1 plays a sound and draws a rect, + // for 0 nothing happens. + + for (int i = 0; i < numsounds; i++){ + // Check which indexes are 1 and 0. + if (playSound[i] == 1){ + float rate; + // Choose a random color and get set to noStroke() + fill(int(random(255)),int(random(255)),int(random(255))); + noStroke(); + // Draw the rect in the positions we defined earlier in posx + rect(posx[i], 50, 100, 200); + // Choose a random index of the octave array + rate = octave[int(random(0,5))]; + // Play the soundfile from the array with the respective rate and loop set to false + file[i].play(rate, 1.0); + } + + // Renew the indexes of playSound so that at the next event the order is different and randomized. + playSound[i] = int(random(0,2)); + }; + + // Create a new triggertime in the future, with a random offset between 200 and 1000 milliseconds + trigger = millis() + int(random(200,1000)); + } +} diff --git a/java/libraries/sound/examples/processing_handbook/ex5/data/beat.aiff b/java/libraries/sound/examples/processing_handbook/ex5/data/beat.aiff new file mode 100644 index 000000000..017b7ce23 Binary files /dev/null and b/java/libraries/sound/examples/processing_handbook/ex5/data/beat.aiff differ diff --git a/java/libraries/sound/examples/processing_handbook/ex5/ex5.pde b/java/libraries/sound/examples/processing_handbook/ex5/ex5.pde new file mode 100644 index 000000000..40a478b47 --- /dev/null +++ b/java/libraries/sound/examples/processing_handbook/ex5/ex5.pde @@ -0,0 +1,47 @@ +/* +This example shows how to use the Fast Fourier Transform function to get the spectrum +of a sound. This function calculates the FFT of a signal and returns the positive normalized +magnitude spectrum. This means we pass it the number of bands we want (the actual FFT size is +two times that size) and a float array with the same size. +*/ + +import processing.sound.*; + +Sound stream; +SoundFile sample; +FFT fft; + +int scale=1; +int bands=512; +float[] spec = new float[bands]; + +public void setup() { + size(bands,350); + background(255); + + // Create and start the sound renderer + stream = new Sound(this, 44100, 256); + + //Load and play a soundfile and loop it. This has to be called + // before the FFT is created. + sample = new SoundFile(this, "beat.aiff"); + sample.play(true); + + // Create and patch the rms tracker + fft = new FFT(this); + fft.input(sample, bands); +} + +public void draw() { + background(255); + + fft.process(spec); + + for(int i = 0; i < bands; i++) + { + // The result of the FFT is normalized + // draw the line for frequency band i scaling it up by 5 to get more amplitude. + line( i, height, i, height - spec[i]*height*5 ); + } +} + diff --git a/java/libraries/sound/library/README.txt b/java/libraries/sound/library/README.txt new file mode 100644 index 000000000..525b4b317 --- /dev/null +++ b/java/libraries/sound/library/README.txt @@ -0,0 +1,22 @@ + + + +# Processing MethCla Interface + +This is a processing interface and a collection of plugins for MethCla, a leight-weight, efficient sound engine for mobile devices [methcla](http://methc.la). + +## Executable + +Download the current version of the library here: + +[Sound](https://github.com/wirsing/ProcessingSound/releases) + + +## Building the libMethClaInterface + +The library requires a compiled shared library of MethCla for each platform. There are specific Makefile in the src folder which compile the JNI library. For the moment this library is OsX only. + +The Java Library is to be compiled with ant. Please install the latest version on ant on your computer. The build.xml file is in resources. To compile do + +$ cd resources +$ ant diff --git a/java/libraries/sound/src/cpp/MakeFile_Linux b/java/libraries/sound/src/cpp/MakeFile_Linux new file mode 100644 index 000000000..52004b60c --- /dev/null +++ b/java/libraries/sound/src/cpp/MakeFile_Linux @@ -0,0 +1,12 @@ +all: + + gcc -fPIC -I/usr/local/java/jdk1.7.0_60/include/linux -I./include -I/usr/local/java/jdk1.7.0_60/include -std=c++11 -g -c processing_sound_MethClaInterface.cpp; + gcc -shared -o libMethClaInterface.so *.o -lmethcla; + +clean: + rm *.o + rm *.jnilib + +install: + cp libMethClaInterface.so ../../library/linux + diff --git a/java/libraries/sound/src/cpp/MakeFile_Win b/java/libraries/sound/src/cpp/MakeFile_Win new file mode 100644 index 000000000..f4816d288 --- /dev/null +++ b/java/libraries/sound/src/cpp/MakeFile_Win @@ -0,0 +1,11 @@ +all: + + g++ -Ic:/Java/jdk1.7.0_60/include -Ic:/Java/jdk1.7.0_60/include/win32 -I./include -std=c++11 -g -c processing_sound_MethClaInterface.cpp; + g++ -dynamiclib -lmethcla -L../../library/macosx/ -o libMethClaInterface.jnilib *.o; + +clean: + rm *.o + rm *.jnilib + +install: + cp libMethClaInterface.jnilib ../../lib/macosx diff --git a/java/libraries/sound/src/cpp/Makefile_Darwin b/java/libraries/sound/src/cpp/Makefile_Darwin new file mode 100644 index 000000000..7ac3036da --- /dev/null +++ b/java/libraries/sound/src/cpp/Makefile_Darwin @@ -0,0 +1,10 @@ +all: + g++ -I/System/Library/Frameworks/JavaVM.framework/Versions/A/Headers -I./include -std=c++11 -g -c processing_sound_MethClaInterface.cpp; + g++ -dynamiclib -lmethcla -L../../library/macosx/ -o libMethClaInterface.jnilib *.o; + +clean: + rm *.o + rm *.jnilib + +install: + cp libMethClaInterface.jnilib ../../library/macosx diff --git a/java/libraries/sound/src/cpp/include/methcla/common.h b/java/libraries/sound/src/cpp/include/methcla/common.h new file mode 100644 index 000000000..2cdb58a5b --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/common.h @@ -0,0 +1,153 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_COMMON_H_INCLUDED +#define METHCLA_COMMON_H_INCLUDED + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(__cplusplus) +# define METHCLA_C_LINKAGE extern "C" +#else +# define METHCLA_C_LINKAGE +#endif + +#if defined _WIN32 || defined __CYGWIN__ + #if defined(BUILDING_DLL) + #if defined(__GNUC__) || defined(__clang__) + #define METHCLA_VISIBLE __attribute__ ((dllexport)) + #else + #define METHCLA_VISIBLE __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + #define METHCLA_VISIBLE __attribute__ ((dllimport)) + #else + #define METHCLA_VISIBLE __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. + #endif + #endif +#else + #if (__GNUC__ >= 4) || (defined(__clang__) && (__clang_major__ >= 4)) + #define METHCLA_VISIBLE __attribute__ ((visibility ("default"))) + #else + #define METHCLA_VISIBLE + #endif +#endif + +#define METHCLA_EXPORT METHCLA_C_LINKAGE METHCLA_VISIBLE + +//* Time in seconds. +typedef double Methcla_Time; + +typedef struct +{ + const void* data; + size_t size; +} Methcla_OSCPacket; + +typedef enum +{ + kMethcla_NoError = 0, + + /* Generic error codes */ + kMethcla_UnspecifiedError, + kMethcla_LogicError, + kMethcla_ArgumentError, + kMethcla_MemoryError, + kMethcla_UnimplementedError, + kMethcla_SystemError, + + /* Engine errors */ + kMethcla_SynthDefNotFoundError = 1000, + kMethcla_NodeIdError, + kMethcla_NodeTypeError, + + /* File errors */ + kMethcla_FileNotFoundError = 2000, + kMethcla_FileExistsError, + kMethcla_PermissionsError, + kMethcla_UnsupportedFileTypeError, + kMethcla_UnsupportedDataFormatError, + kMethcla_InvalidFileError, + + /* Audio driver errors */ + kMethcla_DeviceUnavailableError = 3000, +} Methcla_ErrorCode; + +METHCLA_EXPORT const char* methcla_error_code_description(Methcla_ErrorCode code); + +typedef struct Methcla_Error +{ + Methcla_ErrorCode error_code; + char* error_message; +} Methcla_Error; + +static inline bool methcla_is_ok(const Methcla_Error error) +{ + return error.error_code == kMethcla_NoError; +} + +static inline bool methcla_is_error(const Methcla_Error error) +{ + return error.error_code != kMethcla_NoError; +} + +static inline bool methcla_error_has_code(const Methcla_Error error, Methcla_ErrorCode code) +{ + return error.error_code == code; +} + +static inline Methcla_ErrorCode methcla_error_code(const Methcla_Error error) +{ + return error.error_code; +} + +static inline const char* methcla_error_message(const Methcla_Error error) +{ + return error.error_message; +} + +//* Create a new Methcla_Error with a specific error code. +// The error message is set to NULL. +METHCLA_EXPORT Methcla_Error methcla_error_new(Methcla_ErrorCode code); + +//* Create a new Methcla_Error with a specific error code and message. +METHCLA_EXPORT Methcla_Error methcla_error_new_with_message(Methcla_ErrorCode code, const char* message); + +//* Free the resources associated with a Methcla_Error. +METHCLA_EXPORT void methcla_error_free(Methcla_Error error); + +//* Return a Methcla_Error indicating that no error has occurred. +static inline Methcla_Error methcla_no_error() +{ + return methcla_error_new(kMethcla_NoError); +} + +//* Audio sample type +typedef float Methcla_AudioSample; + +METHCLA_EXPORT void methcla_init(); + +#if defined(__cplusplus) +} +#endif + +#endif /* METHCLA_COMMON_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/detail.hpp b/java/libraries/sound/src/cpp/include/methcla/detail.hpp new file mode 100644 index 000000000..a09a7d960 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/detail.hpp @@ -0,0 +1,98 @@ +// Copyright 2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_DETAIL_HPP_INCLUDED +#define METHCLA_DETAIL_HPP_INCLUDED + +#include +#include +#include + +#include + +namespace Methcla +{ + namespace detail + { + template class Id + { + public: + explicit Id(T id) + : m_id(id) + { } + Id(const D& other) + : m_id(other.m_id) + { } + + T id() const + { + return m_id; + } + + bool operator==(const D& other) const + { + return m_id == other.m_id; + } + + bool operator!=(const D& other) const + { + return m_id != other.m_id; + } + + private: + T m_id; + }; + + inline static void throwError(Methcla_Error err) + { + if (methcla_is_error(err)) + { + if (methcla_error_has_code(err, kMethcla_ArgumentError)) { + std::string msg(methcla_error_message(err)); + methcla_error_free(err); + throw std::invalid_argument(msg); + } else if (methcla_error_has_code(err, kMethcla_LogicError)) { + std::string msg(methcla_error_message(err)); + methcla_error_free(err); + throw std::logic_error(msg); + } else if (methcla_error_has_code(err, kMethcla_MemoryError)) { + methcla_error_free(err); + throw std::bad_alloc(); + } else { + std::string msg( methcla_error_message(err) + ? methcla_error_message(err) + : methcla_error_code_description(methcla_error_code(err))); + methcla_error_free(err); + throw std::runtime_error(msg); + } + } + } + + inline static void checkReturnCode(Methcla_Error err) + { + throwError(err); + } + + template T combineFlags(T a, T b) + { + // FIXME: Not available in GCC 4.6, Clang 3.3 + // typedef typename std::underlying_type::type enum_type; + typedef int enum_type; + static_assert(sizeof(T) <= sizeof(enum_type), "combineFlags: Cannot determine underlying enum type"); + return static_cast(static_cast(a) | static_cast(b)); + } + } +} + +#endif // METHCLA_DETAIL_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/detail/result.hpp b/java/libraries/sound/src/cpp/include/methcla/detail/result.hpp new file mode 100644 index 000000000..bf1bf4e33 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/detail/result.hpp @@ -0,0 +1,171 @@ +// Copyright 2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_DETAIL_RESULT_HPP_INCLUDED +#define METHCLA_DETAIL_RESULT_HPP_INCLUDED + +#include +#include + +#include +#include +#include +#include +#include + +namespace Methcla +{ + namespace detail + { + class ResultBase + { + std::condition_variable m_cond_var; + + protected: + std::mutex m_mutex; + bool m_cond; + Methcla_ErrorCode m_error; + std::string m_errorMessage; + + public: + ResultBase() + : m_cond(false) + , m_error(kMethcla_NoError) + { } + + ResultBase(const ResultBase&) = delete; + ResultBase& operator=(const ResultBase&) = delete; + + void checkResponse(const char* requestAddress, const OSCPP::Server::Message& msg) + { + if (msg == "/error") + { + auto args(msg.args()); + Methcla_ErrorCode errorCode = static_cast(args.int32()); + const char* errorMessage = args.string(); + setError(errorCode, errorMessage); + } + else if (msg != requestAddress) + { + std::stringstream s; + s << "Unexpected response message address " << msg.address() << " (expected " << requestAddress << ")"; + setError(kMethcla_LogicError, s.str().c_str()); + } + } + + protected: + inline void notify() + { + m_cond = true; + m_cond_var.notify_one(); + } + + inline void wait() + { + std::unique_lock lock(m_mutex); + while (!m_cond) { + m_cond_var.wait(lock); + } + if (m_error != kMethcla_NoError) { + throwError(methcla_error_new_with_message(m_error, m_errorMessage.c_str())); + } + } + + void setError(Methcla_ErrorCode error, const char* message) + { + std::lock_guard lock(m_mutex); + if (m_cond) + { + m_error = kMethcla_LogicError; + m_errorMessage = "Result error already set"; + } + else + { + m_error = error; + m_errorMessage = message; + } + notify(); + } + }; + + template class Result : public ResultBase + { + public: + void set(Methcla_ErrorCode error, const char* message) + { + setError(error, message); + } + + void set(const T& value) + { + std::lock_guard lock(m_mutex); + if (m_error == kMethcla_NoError) + { + if (m_cond) + { + m_error = kMethcla_LogicError; + m_errorMessage = "Result already set"; + } + else + { + m_value = value; + notify(); + } + } + } + + const T& get() + { + wait(); + return m_value; + } + + private: + T m_value; + }; + + template <> class Result : public ResultBase + { + public: + void set(Methcla_ErrorCode error, const char* message) + { + setError(error, message); + } + + void set() + { + std::lock_guard lock(m_mutex); + if (m_error == kMethcla_NoError) + { + if (m_cond) + { + m_error = kMethcla_LogicError; + m_errorMessage = "Result already set"; + } + else + { + notify(); + } + } + } + + void get() + { + wait(); + } + }; + } +} + +#endif // METHCLA_DETAIL_RESULT_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/engine.h b/java/libraries/sound/src/cpp/include/methcla/engine.h new file mode 100644 index 000000000..a820885a2 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/engine.h @@ -0,0 +1,155 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_ENGINE_H_INCLUDED +#define METHCLA_ENGINE_H_INCLUDED + +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +//* Return library version string. +const char* methcla_version(); + +//* Return true if using the pro version of methcla. +static inline bool methcla_version_is_pro() +{ + return strstr(methcla_version(), "pro") != NULL; +} + +//* Common audio driver options. +typedef struct Methcla_AudioDriverOptions +{ + int sample_rate; + int num_inputs; + int num_outputs; + int buffer_size; +} Methcla_AudioDriverOptions; + +//* Abstract audio driver type. +typedef struct Methcla_AudioDriver Methcla_AudioDriver; + +//* Initialize audio options. +METHCLA_EXPORT void methcla_audio_driver_options_init(Methcla_AudioDriverOptions* options); + +//* Return default audio driver for this platform. +METHCLA_EXPORT Methcla_Error methcla_default_audio_driver(const Methcla_AudioDriverOptions* options, Methcla_AudioDriver** outDriver); + +//* An integral type for uniquely identifying requests sent to the engine. +typedef int32_t Methcla_RequestId; + +enum +{ + //* Request id reserved for asynchronous notifications. + // Clients should not use this id when sending requests to the engine. + kMethcla_Notification = 0 +}; + +//* Callback closure type for handling OSC packets coming from the engine. +// Packets can be either responses to previously issued requests, or, if request_id is equal to kMethcla_Notification, an asynchronous notification. +typedef struct Methcla_PacketHandler +{ + void* handle; + void (*handle_packet)(void* handle, Methcla_RequestId request_id, const void* packet, size_t size); +} Methcla_PacketHandler; + +typedef struct Methcla_EngineOptions Methcla_EngineOptions; + +struct Methcla_EngineOptions +{ + Methcla_LogHandler log_handler; + Methcla_PacketHandler packet_handler; + + size_t sample_rate; + size_t block_size; + + size_t realtime_memory_size; + size_t max_num_nodes; + size_t max_num_audio_buses; + + //* NULL terminated array of plugin library functions. + Methcla_LibraryFunction* plugin_libraries; +}; + +METHCLA_EXPORT void methcla_engine_options_init(Methcla_EngineOptions* options); + +//* Abstract type for the sound engine. +typedef struct Methcla_Engine Methcla_Engine; + +//* Create a new engine with the given options and an audio driver. +METHCLA_EXPORT Methcla_Error methcla_engine_new_with_driver( + const Methcla_EngineOptions* options, + Methcla_AudioDriver* driver, + Methcla_Engine** engine + ); + +//* Free the resources associated with engine. +// +// Dereferencing engine after this function returns results in undefined behavior. +METHCLA_EXPORT void methcla_engine_free(Methcla_Engine* engine); + +//* Return the last error code. +// METHCLA_EXPORT Methcla_Error methcla_engine_error(const Methcla_Engine* engine); + +//* Start the engine. +METHCLA_EXPORT Methcla_Error methcla_engine_start(Methcla_Engine* engine); + +//* Stop the engine. +METHCLA_EXPORT Methcla_Error methcla_engine_stop(Methcla_Engine* engine); + +enum Methcla_EngineLogFlags +{ + kMethcla_EngineLogDefault = 0x00, + kMethcla_EngineLogDebug = 0x01, + kMethcla_EngineLogRequests = 0x02 +}; + +//* Set flags for debug logging. +METHCLA_EXPORT void methcla_engine_set_log_flags(Methcla_Engine* engine, Methcla_EngineLogFlags flags); + +//* Log a line using the registered log handler. +METHCLA_EXPORT void methcla_engine_log_line(Methcla_Engine* engine, Methcla_LogLevel level, const char* message); + +//* Encode a Methcla_Time value as a 64 bit unsigned integer. +METHCLA_EXPORT uint64_t methcla_time_to_uint64(Methcla_Time time); + +//* Decode a Methcla_Time value from a 64 bit unsigned integer. +METHCLA_EXPORT Methcla_Time methcla_time_from_uint64(uint64_t time); + +//* Get the current time. +METHCLA_EXPORT Methcla_Time methcla_engine_current_time(Methcla_Engine* engine); + +//* Send an OSC packet to the engine. +METHCLA_EXPORT Methcla_Error methcla_engine_send(Methcla_Engine* engine, const void* packet, size_t size); + +//* Open a sound file. +METHCLA_EXPORT Methcla_Error methcla_engine_soundfile_open(const Methcla_Engine* engine, const char* path, Methcla_FileMode mode, Methcla_SoundFile** file, Methcla_SoundFileInfo* info); + +#if defined(__cplusplus) +} +#endif + +#endif /* METHCLA_ENGINE_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/engine.hpp b/java/libraries/sound/src/cpp/include/methcla/engine.hpp new file mode 100644 index 000000000..f42b759c7 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/engine.hpp @@ -0,0 +1,1146 @@ +// Copyright 2012-2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_ENGINE_HPP_INCLUDED +#define METHCLA_ENGINE_HPP_INCLUDED + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Methcla +{ + static inline const char* version() + { + return methcla_version(); + } + + namespace Version + { + static inline bool isPro() + { + return methcla_version_is_pro(); + } + }; + + inline static void dumpRequest(std::ostream& out, const OSCPP::Client::Packet& packet) + { + out << "Request (send): " << packet << std::endl; + } + + class NodeId : public detail::Id + { + public: + NodeId(int32_t id) + : Id(id) + { } + NodeId() + : NodeId(-1) + { } + }; + + class GroupId : public NodeId + { + public: + // Inheriting constructors not supported by clang 3.2 + // using NodeId::NodeId; + GroupId(int32_t id) + : NodeId(id) + { } + GroupId() + : NodeId() + { } + }; + + class SynthId : public NodeId + { + public: + SynthId(int32_t id) + : NodeId(id) + { } + SynthId() + : NodeId() + { } + }; + + class AudioBusId : public detail::Id + { + public: + AudioBusId(int32_t id) + : Id(id) + { } + AudioBusId() + : AudioBusId(0) + { } + }; + + // Node placement specification given a target. + class NodePlacement + { + NodeId m_target; + Methcla_NodePlacement m_placement; + + public: + NodePlacement(NodeId target, Methcla_NodePlacement placement) + : m_target(target) + , m_placement(placement) + { } + + NodePlacement(GroupId target) + : NodePlacement(target, kMethcla_NodePlacementTailOfGroup) + { } + + NodeId target() const + { + return m_target; + } + + Methcla_NodePlacement placement() const + { + return m_placement; + } + + static NodePlacement head(GroupId target) + { + return NodePlacement(target, kMethcla_NodePlacementHeadOfGroup); + } + + static NodePlacement tail(GroupId target) + { + return NodePlacement(target, kMethcla_NodePlacementTailOfGroup); + } + + static NodePlacement before(NodeId target) + { + return NodePlacement(target, kMethcla_NodePlacementBeforeNode); + } + + static NodePlacement after(NodeId target) + { + return NodePlacement(target, kMethcla_NodePlacementAfterNode); + } + }; + + enum BusMappingFlags + { + kBusMappingInternal = kMethcla_BusMappingInternal, + kBusMappingExternal = kMethcla_BusMappingExternal, + kBusMappingFeedback = kMethcla_BusMappingFeedback, + kBusMappingReplace = kMethcla_BusMappingReplace + }; + + static inline BusMappingFlags operator|(BusMappingFlags a, BusMappingFlags b) + { + return detail::combineFlags(a, b); + } + + enum NodeDoneFlags + { + kNodeDoneDoNothing = kMethcla_NodeDoneDoNothing + , kNodeDoneFreeSelf = kMethcla_NodeDoneFreeSelf + , kNodeDoneFreePreceeding = kMethcla_NodeDoneFreePreceeding + , kNodeDoneFreeFollowing = kMethcla_NodeDoneFreeFollowing + , kNodeDoneFreeAllSiblings = kMethcla_NodeDoneFreeAllSiblings + , kNodeDoneFreeParent = kMethcla_NodeDoneFreeParent + }; + + static inline NodeDoneFlags operator|(NodeDoneFlags a, NodeDoneFlags b) + { + return detail::combineFlags(a, b); + } + + struct NodeTreeStatistics + { + size_t numGroups; + size_t numSynths; + + NodeTreeStatistics() + : numGroups(0) + , numSynths(0) + {} + }; + + struct RealtimeMemoryStatistics + { + size_t freeNumBytes; + size_t usedNumBytes; + + RealtimeMemoryStatistics() + : freeNumBytes(0) + , usedNumBytes(0) + {} + + size_t totalNumBytes() const + { + return freeNumBytes + usedNumBytes; + } + }; + + template class ResourceIdAllocator + { + public: + class Statistics + { + size_t m_capacity; + size_t m_allocated; + + public: + Statistics(size_t capacity, size_t allocated) + : m_capacity(capacity) + , m_allocated(allocated) + { } + Statistics(const Statistics&) = default; + + size_t capacity() const { return m_capacity; } + size_t allocated() const { return m_allocated; } + size_t available() const { return capacity() - allocated(); } + }; + + ResourceIdAllocator(T minValue, size_t n) + : m_offset(minValue) + , m_bits(n) + , m_pos(0) + , m_allocated(0) + { } + + Statistics getStatistics() + { + std::lock_guard lock(m_mutex); + return Statistics(m_bits.size(), m_allocated); + } + + Id alloc() + { + std::lock_guard lock(m_mutex); + for (size_t i=m_pos; i < m_bits.size(); i++) { + if (!m_bits[i]) { + m_bits[i] = true; + m_pos = (i+1) == m_bits.size() ? 0 : i+1; + m_allocated++; + return Id(m_offset + i); + } + } + for (size_t i=0; i < m_pos; i++) { + if (!m_bits[i]) { + m_bits[i] = true; + m_pos = i+1; + m_allocated++; + return Id(m_offset + i); + } + } + throw std::runtime_error("No free ids"); + } + + void free(Id id) + { + std::lock_guard lock(m_mutex); + T i = id.id() - m_offset; + if ((i >= 0) && (i < (T)m_bits.size()) && m_bits[i]) { + m_bits[i] = false; + m_allocated--; +#if 0 // Don't throw exception for now + } else { + throw std::runtime_error("Invalid id"); +#endif + } + } + + private: + T m_offset; + std::vector m_bits; + size_t m_pos; + size_t m_allocated; + // TODO: Make lock configurable? + std::mutex m_mutex; + }; + + class PacketPool + { + public: + PacketPool(const PacketPool&) = delete; + PacketPool& operator=(const PacketPool&) = delete; + + PacketPool(size_t packetSize) + : m_packetSize(packetSize) + { } + ~PacketPool() + { + std::lock_guard lock(m_mutex); + while (!m_freeList.empty()) { + void* ptr = m_freeList.front(); + delete [] (char*)ptr; + m_freeList.pop_front(); + } + } + + size_t packetSize() const + { + return m_packetSize; + } + + void* alloc() + { + std::lock_guard lock(m_mutex); + if (m_freeList.empty()) + return new char[m_packetSize]; + void* result = m_freeList.back(); + m_freeList.pop_back(); + return result; + } + + void free(void* ptr) + { + std::lock_guard lock(m_mutex); + m_freeList.push_back(ptr); + } + + private: + size_t m_packetSize; + // TODO: Use boost::lockfree::queue for free list + std::list m_freeList; + std::mutex m_mutex; + }; + + class Packet + { + public: + Packet(PacketPool& pool) + : m_pool(pool) + , m_packet(pool.alloc(), pool.packetSize()) + { } + ~Packet() + { + m_pool.free(m_packet.data()); + } + + Packet(const Packet&) = delete; + Packet& operator=(const Packet&) = delete; + + const OSCPP::Client::Packet& packet() const + { + return m_packet; + } + + OSCPP::Client::Packet& packet() + { + return m_packet; + } + + private: + PacketPool& m_pool; + OSCPP::Client::Packet m_packet; + }; + + class Value + { + public: + enum Type + { + kInt, + kFloat, + kString + }; + + explicit Value(int x) : m_type(kInt), m_int(x) {} + explicit Value(bool x) : m_type(kInt), m_int(x) { } + explicit Value(float x) : m_type(kFloat), m_float(x) {} + explicit Value(double x) : Value((float)x) {} + explicit Value(const std::string& x) : m_type(kString), m_string(x) {} + explicit Value(const char* x) : Value(std::string(x)) {} + + void put(OSCPP::Client::Packet& packet) const + { + switch (m_type) { + case kInt: + packet.int32(m_int); + break; + case kFloat: + packet.float32(m_float); + break; + case kString: + packet.string(m_string.c_str()); + break; + } + } + + private: + Type m_type; + int m_int; + float m_float; + std::string m_string; + }; + + typedef Methcla_LibraryFunction LibraryFunction; + + template class Optional + { + bool m_isSet; + T m_value; + + public: + Optional() + : m_isSet(false) + { } + Optional(const T& value) + : m_isSet(true) + , m_value(value) + { } + Optional(const Optional& other) = default; + + bool isSet() const + { + return m_isSet; + } + + const T& value(const T& def) const + { + return isSet() ? m_value : def; + } + + const T& value() const + { + if (!isSet()) + throw std::logic_error("Optional value unset"); + return m_value; + } + }; + + typedef std::function LogHandler; + + class AudioDriverOptions + { + public: + Optional sampleRate; + Optional numInputs; + Optional numOutputs; + Optional bufferSize; + + operator Methcla_AudioDriverOptions() const + { + Methcla_AudioDriverOptions result; + methcla_audio_driver_options_init(&result); + result.sample_rate = sampleRate.value(-1); + result.num_inputs = numInputs.value(-1); + result.num_outputs = numOutputs.value(-1); + result.buffer_size = bufferSize.value(-1); + return result; + } + }; + + class EngineOptions + { + Methcla_EngineOptions m_options; + std::vector m_pluginLibraries; + + public: + LogHandler logHandler; + Methcla_EngineLogFlags logFlags = kMethcla_EngineLogDefault; + + size_t realtimeMemorySize = 1024*1024; + size_t maxNumNodes = 1024; + size_t maxNumAudioBuses = 1024; + size_t maxNumControlBuses = 4096; + size_t sampleRate = 44100; + size_t blockSize = 64; + std::list pluginLibraries; + + AudioDriverOptions audioDriver; + + EngineOptions& addLibrary(LibraryFunction pluginLibrary) + { + pluginLibraries.push_back(pluginLibrary); + return *this; + } + + Methcla_EngineOptions& options() + { + methcla_engine_options_init(&m_options); + + m_options.sample_rate = sampleRate; + m_options.block_size = blockSize; + m_options.realtime_memory_size = realtimeMemorySize; + m_options.max_num_nodes = maxNumNodes; + m_options.max_num_audio_buses = maxNumAudioBuses; + + m_pluginLibraries.assign(pluginLibraries.begin(), pluginLibraries.end()); + m_pluginLibraries.push_back(nullptr); + + m_options.plugin_libraries = m_pluginLibraries.data(); + + return m_options; + } + }; + + static const Methcla_Time immediately = 0.; + + typedef ResourceIdAllocator NodeIdAllocator; + typedef ResourceIdAllocator AudioBusIdAllocator; + + class Request; + + class EngineInterface + { + public: + virtual ~EngineInterface() { } + + GroupId root() const + { + return GroupId(0); + } + + virtual NodeIdAllocator& nodeIdAllocator() = 0; + + virtual std::unique_ptr allocPacket() = 0; + virtual void sendPacket(const std::unique_ptr& packet) = 0; + + inline void bundle(Methcla_Time time, std::function func); + + inline GroupId group(const NodePlacement& placement); + inline void freeAll(GroupId group); + inline SynthId synth(const char* synthDef, const NodePlacement& placement, const std::vector& controls, const std::list& options=std::list()); + inline void activate(SynthId synth); + inline void mapInput(SynthId synth, size_t index, AudioBusId bus, BusMappingFlags flags=kBusMappingInternal); + inline void mapOutput(SynthId synth, size_t index, AudioBusId bus, BusMappingFlags flags=kBusMappingInternal); + inline void set(NodeId node, size_t index, double value); + inline void free(NodeId node); + }; + + class Request + { + struct Flags + { + bool isMessage : 1; + bool isBundle : 1; + bool isClosed : 1; + }; + + EngineInterface* m_engine; + std::unique_ptr m_packet; + size_t m_bundleCount; + Flags m_flags; + + private: + void beginMessage() + { + if (m_flags.isMessage) + throw std::runtime_error("Cannot add more than one message to non-bundle packet"); + else if (m_flags.isBundle && m_flags.isClosed) + throw std::runtime_error("Cannot add message to closed top-level bundle"); + else if (!m_flags.isBundle) + m_flags.isMessage = true; + } + + OSCPP::Client::Packet& oscPacket() + { + return m_packet->packet(); + } + + public: + Request(EngineInterface* engine) + : m_engine(engine) + , m_packet(engine->allocPacket()) + , m_bundleCount(0) + { + m_flags.isMessage = false; + m_flags.isBundle = false; + m_flags.isClosed = false; + } + + Request(EngineInterface& engine) + : Request(&engine) + { } + + Request(const Request&) = delete; + Request& operator=(const Request&) = delete; + + //* Return size of request packet in bytes. + size_t size() const + { + return m_packet->packet().size(); + } + + void openBundle(Methcla_Time time=immediately) + { + if (m_flags.isMessage) + { + throw std::runtime_error("Cannot open bundle within message packet"); + } + else + { + m_flags.isBundle = true; + m_bundleCount++; + oscPacket().openBundle(methcla_time_to_uint64(time)); + } + } + + // Close nested bundle + void closeBundle() + { + if (m_flags.isMessage) + { + throw std::runtime_error("closeBundle called on a message request"); + } + else if (m_bundleCount == 0) + { + throw std::runtime_error("closeBundle without matching openBundle"); + } + else + { + oscPacket().closeBundle(); + m_bundleCount--; + if (m_bundleCount == 0) + m_flags.isClosed = true; + } + } + + void bundle(Methcla_Time time, std::function func) + { + openBundle(time); + func(*this); + closeBundle(); + } + + //* Finalize request and send to the engine. + void send() + { + if (m_flags.isBundle && m_bundleCount > 0) + throw std::runtime_error("openBundle without matching closeBundle"); + m_engine->sendPacket(m_packet); + } + + GroupId group(const NodePlacement& placement) + { + beginMessage(); + + const NodeId nodeId(m_engine->nodeIdAllocator().alloc()); + + oscPacket() + .openMessage("/group/new", 3) + .int32(nodeId.id()) + .int32(placement.target().id()) + .int32(placement.placement()) + .closeMessage(); + + return GroupId(nodeId.id()); + } + + void freeAll(GroupId group) + { + beginMessage(); + + oscPacket() + .openMessage("/group/freeAll", 1) + .int32(group.id()) + .closeMessage(); + } + + SynthId synth(const char* synthDef, const NodePlacement& placement, const std::vector& controls, const std::list& options=std::list()) + { + beginMessage(); + + const NodeId nodeId(m_engine->nodeIdAllocator().alloc()); + + oscPacket() + .openMessage("/synth/new", 4 + OSCPP::Tags::array(controls.size()) + OSCPP::Tags::array(options.size())) + .string(synthDef) + .int32(nodeId.id()) + .int32(placement.target().id()) + .int32(placement.placement()) + .putArray(controls.begin(), controls.end()); + + oscPacket().openArray(); + for (const auto& x : options) { + x.put(oscPacket()); + } + oscPacket().closeArray(); + + oscPacket().closeMessage(); + + return SynthId(nodeId.id()); + } + + void activate(SynthId synth) + { + beginMessage(); + + oscPacket() + .openMessage("/synth/activate", 1) + .int32(synth.id()) + .closeMessage(); + } + + void mapInput(SynthId synth, size_t index, AudioBusId bus, BusMappingFlags flags=kBusMappingInternal) + { + beginMessage(); + + oscPacket() + .openMessage("/synth/map/input", 4) + .int32(synth.id()) + .int32(index) + .int32(bus.id()) + .int32(flags) + .closeMessage(); + } + + void mapOutput(SynthId synth, size_t index, AudioBusId bus, BusMappingFlags flags=kBusMappingInternal) + { + beginMessage(); + + oscPacket() + .openMessage("/synth/map/output", 4) + .int32(synth.id()) + .int32(index) + .int32(bus.id()) + .int32(flags) + .closeMessage(); + } + + void set(NodeId node, size_t index, double value) + { + beginMessage(); + + oscPacket() + .openMessage("/node/set", 3) + .int32(node.id()) + .int32(index) + .float32(value) + .closeMessage(); + } + + void free(NodeId node) + { + beginMessage(); + + oscPacket() + .openMessage("/node/free", 1) + .int32(node.id()) + .closeMessage(); + m_engine->nodeIdAllocator().free(node.id()); + } + + void whenDone(SynthId synth, NodeDoneFlags flags) + { + beginMessage(); + + oscPacket() + .openMessage("/synth/property/doneFlags/set", 2) + .int32(synth.id()) + .int32(flags) + .closeMessage(); + } + }; + + void EngineInterface::bundle(Methcla_Time time, std::function func) + { + Request request(this); + request.bundle(time, func); + request.send(); + } + + GroupId EngineInterface::group(const NodePlacement& placement) + { + Request request(this); + GroupId result = request.group(placement); + request.send(); + return result; + } + + void EngineInterface::freeAll(GroupId group) + { + Request request(this); + request.freeAll(group); + request.send(); + } + + SynthId EngineInterface::synth(const char* synthDef, const NodePlacement& placement, const std::vector& controls, const std::list& options) + { + Request request(this); + SynthId result = request.synth(synthDef, placement, controls, options); + request.send(); + return result; + } + + void EngineInterface::activate(SynthId synth) + { + Request request(this); + request.activate(synth); + request.send(); + } + + void EngineInterface::mapInput(SynthId synth, size_t index, AudioBusId bus, BusMappingFlags flags) + { + Request request(this); + request.mapInput(synth, index, bus, flags); + request.send(); + } + + void EngineInterface::mapOutput(SynthId synth, size_t index, AudioBusId bus, BusMappingFlags flags) + { + Request request(this); + request.mapOutput(synth, index, bus, flags); + request.send(); + } + + void EngineInterface::set(NodeId node, size_t index, double value) + { + Request request(this); + request.set(node, index, value); + request.send(); + } + + void EngineInterface::free(NodeId node) + { + Request request(this); + request.free(node); + request.send(); + } + + class Engine : public EngineInterface + { + public: + Engine(EngineOptions inOptions=EngineOptions(), Methcla_AudioDriver* driver=nullptr) + : m_logHandler(inOptions.logHandler) + , m_nodeIds(1, inOptions.maxNumNodes - 1) + , m_audioBusIds(0, inOptions.maxNumAudioBuses) + , m_requestId(kMethcla_Notification+1) + , m_notificationHandlerId(0) + , m_packets(8192) + { + Methcla_EngineOptions& options = inOptions.options(); + + if (m_logHandler != nullptr) + { + options.log_handler.handle = this; + options.log_handler.log_line = logLineCallback; + } + + options.packet_handler.handle = this; + options.packet_handler.handle_packet = handlePacket; + + if (driver == nullptr) { + Methcla_AudioDriverOptions driverOptions(inOptions.audioDriver); + detail::checkReturnCode(methcla_default_audio_driver(&driverOptions, &driver)); + } + + detail::checkReturnCode( + methcla_engine_new_with_driver(&options, driver, &m_engine) + ); + + methcla_engine_set_log_flags(m_engine, inOptions.logFlags); + } + + ~Engine() + { + methcla_engine_free(m_engine); + } + + operator const Methcla_Engine* () const + { + return m_engine; + } + + operator Methcla_Engine* () + { + return m_engine; + } + + void start() + { + detail::checkReturnCode(methcla_engine_start(m_engine)); + } + + void stop() + { + detail::checkReturnCode(methcla_engine_stop(m_engine)); + } + + Methcla_Time currentTime() + { + return methcla_engine_current_time(m_engine); + } + + void setLogFlags(Methcla_EngineLogFlags flags) + { + methcla_engine_set_log_flags(m_engine, flags); + } + + void logLine(Methcla_LogLevel level, const char* message) + { + methcla_engine_log_line(m_engine, level, message); + } + + void logLine(Methcla_LogLevel level, const std::string& message) + { + logLine(level, message.c_str()); + } + + NodeIdAllocator& nodeIdAllocator() override + { + return m_nodeIds; + } + + AudioBusIdAllocator& audioBusId() + { + return m_audioBusIds; + } + + std::unique_ptr allocPacket() override + { + return std::unique_ptr(new Packet(m_packets)); + } + + void sendPacket(const std::unique_ptr& packet) override + { + send(*packet); + } + + typedef std::function NotificationHandler; + typedef uint64_t NotificationHandlerId; + + NotificationHandlerId addNotificationHandler(NotificationHandler handler) + { + std::lock_guard lock(m_notificationHandlersMutex); + NotificationHandlerId handlerId = m_notificationHandlerId; + m_notificationHandlers[handlerId] = handler; + m_notificationHandlerId++; + return handlerId; + } + + void removeNotificationHandler(NotificationHandlerId handlerId) + { + std::lock_guard lock(m_notificationHandlersMutex); + m_notificationHandlers.erase(handlerId); + } + + NotificationHandler freeNodeIdHandler(NodeId nodeId) + { + return [this,nodeId](const OSCPP::Server::Message& msg) { + if (msg == "/node/ended") + { + NodeId otherNodeId = NodeId(msg.args().int32()); + if (nodeId == otherNodeId) + { + nodeIdAllocator().free(nodeId); + return true; + } + } + return false; + }; + } + + NotificationHandler freeNodeIdHandler(NodeId nodeId, std::function whenDone) + { + return [this,nodeId,whenDone](const OSCPP::Server::Message& msg) { + if (msg == "/node/ended") + { + NodeId otherNodeId = NodeId(msg.args().int32()); + if (nodeId == otherNodeId) + { + nodeIdAllocator().free(nodeId); + whenDone(nodeId); + return true; + } + } + return false; + }; + } + + NodeTreeStatistics getNodeTreeStatistics() + { + const char* request = "/node/tree/statistics"; + const Methcla_RequestId requestId = getRequestId(); + std::unique_ptr packet = allocPacket(); + packet->packet() + .openMessage(request, 1) + .int32(requestId) + .closeMessage(); + detail::Result result; + withRequest(requestId, packet->packet(), [&request,&result](Methcla_RequestId, const OSCPP::Server::Message& response){ + result.checkResponse(request, response); + OSCPP::Server::ArgStream args(response.args()); + NodeTreeStatistics value; + value.numGroups = args.int32(); + value.numSynths = args.int32(); + result.set(value); + }); + return result.get(); + } + + RealtimeMemoryStatistics getRealtimeMemoryStatistics() + { + const char* request = "/engine/realtime-memory/statistics"; + const Methcla_RequestId requestId = getRequestId(); + auto packet = allocPacket(); + packet->packet() + .openMessage(request, 1) + .int32(requestId) + .closeMessage(); + detail::Result result; + withRequest(requestId, packet->packet(), [&request,&result](Methcla_RequestId, const OSCPP::Server::Message& response){ + result.checkResponse(request, response); + OSCPP::Server::ArgStream args(response.args()); + RealtimeMemoryStatistics value; + value.freeNumBytes = args.int32(); + value.usedNumBytes = args.int32(); + result.set(value); + }); + return result.get(); + } + + private: + static void logLineCallback(void* data, Methcla_LogLevel level, const char* message) + { + assert( data != nullptr ); + static_cast(data)->m_logHandler(level, message); + } + + static void handlePacket(void* data, Methcla_RequestId requestId, const void* packet, size_t size) + { + if (requestId == kMethcla_Notification) + static_cast(data)->handleNotification(packet, size); + else + static_cast(data)->handleReply(requestId, packet, size); + } + + void handleNotification(const void* packet, size_t size) + { + // Parse notification packet + OSCPP::Server::Message message(OSCPP::Server::Packet(packet, size)); + + // Broadcast notification to handlers + std::lock_guard lock(m_notificationHandlersMutex); + for (auto it=m_notificationHandlers.begin(); it != m_notificationHandlers.end();) + { + if (it->second(message)) it = m_notificationHandlers.erase(it); + else it++; + } + } + + void handleReply(Methcla_RequestId requestId, const void* packet, size_t size) + { + // Parse response packet + OSCPP::Server::Message message(OSCPP::Server::Packet(packet, size)); + + // Look up request id and invoke callback + std::lock_guard lock(m_responseHandlersMutex); + + auto it = m_responseHandlers.find(requestId); + if (it != m_responseHandlers.end()) + { + try + { + it->second(requestId, message); + m_responseHandlers.erase(it); + } + catch (...) + { + m_responseHandlers.erase(it); + throw; + } + } + } + + void send(const void* packet, size_t size) + { + detail::checkReturnCode(methcla_engine_send(m_engine, packet, size)); + } + + void send(const OSCPP::Client::Packet& packet) + { + // dumpRequest(std::cout, packet); + send(packet.data(), packet.size()); + } + + void send(const Packet& packet) + { + send(packet.packet()); + } + + Methcla_RequestId getRequestId() + { + std::lock_guard lock(m_requestIdMutex); + Methcla_RequestId result = m_requestId; + if (result == kMethcla_Notification) { + result++; + } + m_requestId = result + 1; + return result; + } + + typedef std::function ResponseHandler; + + void addResponseHandler(Methcla_RequestId requestId, ResponseHandler handler) + { + std::lock_guard lock(m_responseHandlersMutex); + if (m_responseHandlers.find(requestId) != m_responseHandlers.end()) { + throw std::logic_error("Methcla::Engine::addResponseHandler: Duplicate request id"); + } + m_responseHandlers[requestId] = handler; + } + + void withRequest(Methcla_RequestId requestId, const OSCPP::Client::Packet& request, ResponseHandler handler) + { + addResponseHandler(requestId, handler); + send(request); + } + + void execRequest(const char* requestAddress, Methcla_RequestId requestId, const OSCPP::Client::Packet& request) + { + detail::Result result; + withRequest(requestId, request, [requestAddress,&result](Methcla_RequestId, const OSCPP::Server::Message& response){ + result.checkResponse(requestAddress, response); + result.set(); + }); + result.get(); + } + + private: + typedef std::unordered_map ResponseHandlers; + typedef std::unordered_map NotificationHandlers; + + Methcla_Engine* m_engine; + LogHandler m_logHandler; + NodeIdAllocator m_nodeIds; + AudioBusIdAllocator m_audioBusIds; + Methcla_RequestId m_requestId; + std::mutex m_requestIdMutex; + ResponseHandlers m_responseHandlers; + std::mutex m_responseHandlersMutex; + NotificationHandlers m_notificationHandlers; + NotificationHandlerId m_notificationHandlerId; + std::mutex m_notificationHandlersMutex; + PacketPool m_packets; + }; +}; + +#endif // METHCLA_ENGINE_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/file.h b/java/libraries/sound/src/cpp/include/methcla/file.h new file mode 100644 index 000000000..532f64979 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/file.h @@ -0,0 +1,121 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_FILE_H_INCLUDED +#define METHCLA_FILE_H_INCLUDED + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum +{ + kMethcla_FileModeRead, + kMethcla_FileModeWrite +} Methcla_FileMode; + +typedef enum +{ + kMethcla_SoundFileTypeUnknown, + kMethcla_SoundFileTypeAIFF, + kMethcla_SoundFileTypeWAV +} Methcla_SoundFileType; + +typedef enum +{ + kMethcla_SoundFileFormatUnknown, + kMethcla_SoundFileFormatPCM16, + kMethcla_SoundFileFormatPCM24, + kMethcla_SoundFileFormatPCM32, + kMethcla_SoundFileFormatFloat +} Methcla_SoundFileFormat; + +typedef struct +{ + int64_t frames; + unsigned int channels; + unsigned int samplerate; + Methcla_SoundFileType file_type; + Methcla_SoundFileFormat file_format; +} Methcla_SoundFileInfo; + +typedef struct Methcla_SoundFile Methcla_SoundFile; + +struct Methcla_SoundFile +{ + void* handle; + Methcla_Error (*close)(const Methcla_SoundFile* file); + Methcla_Error (*seek)(const Methcla_SoundFile* file, int64_t numFrames); + Methcla_Error (*tell)(const Methcla_SoundFile* file, int64_t* numFrames); + Methcla_Error (*read_float)(const Methcla_SoundFile* file, float* buffer, size_t numFrames, size_t* outNumFrames); + Methcla_Error (*write_float)(const Methcla_SoundFile* file, const float* buffer, size_t numFrames, size_t* outNumFrames); +}; + +typedef struct Methcla_SoundFileAPI Methcla_SoundFileAPI; + +struct Methcla_SoundFileAPI +{ + void* handle; + Methcla_Error (*open)(const Methcla_SoundFileAPI* api, const char* path, Methcla_FileMode mode, Methcla_SoundFile** file, Methcla_SoundFileInfo* info); +}; + +static inline Methcla_Error methcla_soundfile_close(Methcla_SoundFile* file) +{ + if ((file == NULL) || (file->close == NULL)) + return methcla_error_new(kMethcla_ArgumentError); + return file->close(file); +} + +static inline Methcla_Error methcla_soundfile_seek(Methcla_SoundFile* file, int64_t numFrames) +{ + if ((file == NULL) || (file->seek == NULL)) + return methcla_error_new(kMethcla_ArgumentError); + return file->seek(file, numFrames); +} + +static inline Methcla_Error methcla_soundfile_tell(Methcla_SoundFile* file, int64_t* numFrames) +{ + if ((file == NULL) || (file->tell == NULL) || (numFrames == NULL)) + return methcla_error_new(kMethcla_ArgumentError); + return file->tell(file, numFrames); +} + +static inline Methcla_Error methcla_soundfile_read_float(Methcla_SoundFile* file, float* buffer, size_t numFrames, size_t* outNumFrames) +{ + if ((file == NULL) || (file->read_float == NULL) || + (buffer == NULL) || (outNumFrames == NULL)) + return methcla_error_new(kMethcla_ArgumentError); + return file->read_float(file, buffer, numFrames, outNumFrames); +} + +static inline Methcla_Error methcla_soundfile_write_float(Methcla_SoundFile* file, const float* buffer, size_t numFrames, size_t* outNumFrames) +{ + if ((file == NULL) || (file->write_float == NULL) || + (buffer == NULL) || (outNumFrames == NULL)) + return methcla_error_new(kMethcla_ArgumentError); + return file->write_float(file, buffer, numFrames, outNumFrames); +} + +#if defined(__cplusplus) +} +#endif + +#endif /* METHCLA_FILE_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/file.hpp b/java/libraries/sound/src/cpp/include/methcla/file.hpp new file mode 100644 index 000000000..f9b07fe78 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/file.hpp @@ -0,0 +1,185 @@ +// Copyright 2012-2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_FILE_HPP_INCLUDED +#define METHCLA_FILE_HPP_INCLUDED + +#include +#include +#include +#include + +#include + +namespace Methcla +{ + class SoundFileInfo : public Methcla_SoundFileInfo + { + public: + SoundFileInfo() + { + frames = 0; + channels = 0; + samplerate = 0; + file_type = kMethcla_SoundFileTypeUnknown; + file_format = kMethcla_SoundFileFormatUnknown; + } + + SoundFileInfo(const Methcla_SoundFileInfo& info) + { + frames = info.frames; + channels = info.channels; + samplerate = info.samplerate; + file_type = info.file_type; + file_format = info.file_format; + } + + int64_t samples() const + { + return channels * frames; + } + + template T duration() const + { + return (T)frames/(T)samplerate; + } + }; + + class SoundFile + { + Methcla_SoundFile* m_file; + SoundFileInfo m_info; + + inline void ensureInitialized() const + { + if (!m_file) + throw std::logic_error("SoundFile has not been initialized"); + } + + public: + SoundFile() + : m_file(nullptr) + {} + + SoundFile(Methcla_SoundFile* file, const Methcla_SoundFileInfo& info) + : m_file(file) + , m_info(info) + {} + + SoundFile(const Engine& engine, const std::string& path) + { + detail::checkReturnCode( + methcla_engine_soundfile_open(engine, path.c_str(), kMethcla_FileModeRead, &m_file, &m_info) + ); + } + + SoundFile(const Engine& engine, const std::string& path, const SoundFileInfo& info) + : m_info(info) + { + detail::checkReturnCode( + methcla_engine_soundfile_open(engine, path.c_str(), kMethcla_FileModeWrite, &m_file, &m_info) + ); + } + + SoundFile(const Methcla_Host* host, const std::string& path) + { + detail::checkReturnCode( + methcla_host_soundfile_open(host, path.c_str(), kMethcla_FileModeRead, &m_file, &m_info) + ); + } + + SoundFile(const Methcla_Host* host, const std::string& path, const SoundFileInfo& info) + : m_info(info) + { + detail::checkReturnCode( + methcla_host_soundfile_open(host, path.c_str(), kMethcla_FileModeWrite, &m_file, &m_info) + ); + } + + // SoundFile is moveable + SoundFile(SoundFile&& other) + : m_file(std::move(other.m_file)) + , m_info(std::move(other.m_info)) + { + other.m_file = nullptr; + } + + SoundFile& operator=(SoundFile&& other) + { + m_file = std::move(other.m_file); + m_info = std::move(other.m_info); + other.m_file = nullptr; + return *this; + } + + // SoundFile is not copyable + SoundFile(const SoundFile&) = delete; + SoundFile& operator=(const SoundFile&) = delete; + + ~SoundFile() + { + if (m_file != nullptr) + methcla_soundfile_close(m_file); + } + + operator bool() const + { + return m_file != nullptr; + } + + const SoundFileInfo& info() const + { + return m_info; + } + + void close() + { + ensureInitialized(); + detail::checkReturnCode(methcla_soundfile_close(m_file)); + m_file = nullptr; + } + + void seek(int64_t numFrames) + { + ensureInitialized(); + detail::checkReturnCode(methcla_soundfile_seek(m_file, numFrames)); + } + + int64_t tell() + { + ensureInitialized(); + int64_t numFrames; + detail::checkReturnCode(methcla_soundfile_tell(m_file, &numFrames)); + return numFrames; + } + + size_t read(float* buffer, size_t numFrames) + { + ensureInitialized(); + size_t outNumFrames; + detail::checkReturnCode(methcla_soundfile_read_float(m_file, buffer, numFrames, &outNumFrames)); + return outNumFrames; + } + + size_t write(const float* buffer, size_t numFrames) + { + ensureInitialized(); + size_t outNumFrames; + detail::checkReturnCode(methcla_soundfile_write_float(m_file, buffer, numFrames, &outNumFrames)); + return outNumFrames; + } + }; +} + +#endif // METHCLA_FILE_HPP_INCLUDED \ No newline at end of file diff --git a/java/libraries/sound/src/cpp/include/methcla/log.h b/java/libraries/sound/src/cpp/include/methcla/log.h new file mode 100644 index 000000000..6525c4649 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/log.h @@ -0,0 +1,34 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_LOG_H_INCLUDED +#define METHCLA_LOG_H_INCLUDED + +typedef enum Methcla_LogLevel +{ + kMethcla_LogError, + kMethcla_LogWarn, + kMethcla_LogInfo, + kMethcla_LogDebug +} Methcla_LogLevel; + +typedef struct Methcla_LogHandler +{ + void* handle; + void (*log_line)(void* handle, Methcla_LogLevel level, const char* message); +} Methcla_LogHandler; + +#endif /* METHCLA_LOG_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/log.hpp b/java/libraries/sound/src/cpp/include/methcla/log.hpp new file mode 100644 index 000000000..51e0658d4 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/log.hpp @@ -0,0 +1,81 @@ +// Copyright 2014 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_LOG_HPP_INCLUDED +#define METHCLA_LOG_HPP_INCLUDED + +#include + +#include +#include +#include + +namespace Methcla { + +class LogStream +{ + Methcla_LogLevel m_level; + std::function m_callback; + std::stringstream* m_stream; + +public: + LogStream(std::function callback, Methcla_LogLevel messageLevel, Methcla_LogLevel currentLevel) + : m_level(messageLevel) + , m_callback(messageLevel <= currentLevel ? callback : nullptr) + , m_stream(nullptr) + {} + + LogStream(std::function callback, Methcla_LogLevel messageLevel) + : LogStream(callback, messageLevel, messageLevel) + {} + + LogStream(const LogStream& other) + : m_level(other.m_level) + , m_callback(other.m_callback) + , m_stream(other.m_stream ? new std::stringstream(other.m_stream->str()) : nullptr) + {} + + ~LogStream() + { + if (m_stream) + { + try + { + if (m_callback) + m_callback(m_level, m_stream->str().c_str()); + delete m_stream; + } + catch (...) + { + delete m_stream; + throw; + } + } + } + + template LogStream& operator<<(const T& x) + { + if (m_callback) + { + if (!m_stream) + m_stream = new std::stringstream(); + *m_stream << x; + } + return *this; + } +}; + +} // namespace Methcla + +#endif // METHCLA_LOG_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/platform/pepper.hpp b/java/libraries/sound/src/cpp/include/methcla/platform/pepper.hpp new file mode 100644 index 000000000..5d5413f5a --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/platform/pepper.hpp @@ -0,0 +1,27 @@ +// Copyright 2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_PLATFORM_PEPPER_HPP_INCLUDED +#define METHCLA_PLATFORM_PEPPER_HPP_INCLUDED + +#include +#include +#include "ppapi/cpp/instance_handle.h" + +METHCLA_EXPORT Methcla_AudioDriver* methcla_platform_pepper_audio_driver_new( + const Methcla_AudioDriverOptions* options, + const pp::InstanceHandle& instance + ); + +#endif // METHCLA_PLATFORM_PEPPER_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/platform/rtaudio.hpp b/java/libraries/sound/src/cpp/include/methcla/platform/rtaudio.hpp new file mode 100644 index 000000000..d1b39f625 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/platform/rtaudio.hpp @@ -0,0 +1,24 @@ +// Copyright 2014 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_PLATFORM_RTAUDIO_HPP_INCLUDED +#define METHCLA_PLATFORM_RTAUDIO_HPP_INCLUDED + +#include + +METHCLA_EXPORT Methcla_AudioDriver* methcla_rtaudio_driver_new( + const Methcla_AudioDriverOptions* options + ); + +#endif // METHCLA_PLATFORM_RTAUDIO_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/plugin.h b/java/libraries/sound/src/cpp/include/methcla/plugin.h new file mode 100644 index 000000000..2ff03dd6a --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugin.h @@ -0,0 +1,322 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGIN_H_INCLUDED +#define METHCLA_PLUGIN_H_INCLUDED + +#include +#include +#include + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define METHCLA_PLUGINS_URI "http://methc.la/plugins" + +//* Realtime interface. +typedef struct Methcla_World Methcla_World; + +//* Non-realtime interface. +typedef struct Methcla_Host Methcla_Host; + +//* Synth handle managed by a plugin. +typedef void Methcla_Synth; + +//* Callback function type for performing commands in the non-realtime context. +typedef void (*Methcla_HostPerformFunction)(const Methcla_Host* host, void* data); + +//* Callback function type for performing commands in the realtime context. +typedef void (*Methcla_WorldPerformFunction)(const Methcla_World* world, void* data); + +//* Realtime interface +struct Methcla_World +{ + //* Handle for implementation specific data. + void* handle; + + //* Return engine sample rate. + double (*samplerate)(const Methcla_World*); + + //* Return maximum audio block size. + size_t (*block_size)(const Methcla_World* world); + + //* Return the time at the start of the current audio block in seconds. + Methcla_Time (*current_time)(const struct Methcla_World* world); + + // Realtime memory allocation + void* (*alloc)(const struct Methcla_World* world, size_t size); + void* (*alloc_aligned)(const struct Methcla_World* world, size_t alignment, size_t size); + void (*free)(const struct Methcla_World* world, void* ptr); + + //* Schedule a command for execution in the non-realtime context. + void (*perform_command)(const Methcla_World* world, Methcla_HostPerformFunction perform, void* data); + + //* Log a message and a newline character. + void (*log_line)(const Methcla_World* world, Methcla_LogLevel level, const char* message); + + //* Free synth. + void (*synth_done)(const struct Methcla_World* world, Methcla_Synth* synth); +}; + +static inline double methcla_world_samplerate(const Methcla_World* world) +{ + assert(world && world->samplerate); + return world->samplerate(world); +} + +static inline size_t methcla_world_block_size(const Methcla_World* world) +{ + assert(world && world->block_size); + return world->block_size(world); +} + +static inline Methcla_Time methcla_world_current_time(const Methcla_World* world) +{ + assert(world); + assert(world->current_time); + return world->current_time(world); +} + +static inline void* methcla_world_alloc(const Methcla_World* world, size_t size) +{ + assert(world && world->alloc); + return world->alloc(world, size); +} + +static inline void* methcla_world_alloc_aligned(const Methcla_World* world, size_t alignment, size_t size) +{ + assert(world && world->alloc_aligned); + return world->alloc_aligned(world, alignment, size); +} + +static inline void methcla_world_free(const Methcla_World* world, void* ptr) +{ + assert(world && world->free); + world->free(world, ptr); +} + +static inline void methcla_world_perform_command(const Methcla_World* world, Methcla_HostPerformFunction perform, void* data) +{ + assert(world && world->perform_command); + assert(perform); + world->perform_command(world, perform, data); +} + +static inline void methcla_world_log_line(const Methcla_World* world, Methcla_LogLevel level, const char* message) +{ + assert(world); + assert(world->log_line); + assert(message); + world->log_line(world, level, message); +} + +static inline void methcla_world_synth_done(const Methcla_World* world, Methcla_Synth* synth) +{ + assert(world); + assert(world->synth_done); + assert(synth); + world->synth_done(world, synth); +} + +typedef enum +{ + kMethcla_Input, + kMethcla_Output +} Methcla_PortDirection; + +typedef enum +{ + kMethcla_ControlPort, + kMethcla_AudioPort +} Methcla_PortType; + +typedef enum +{ + kMethcla_PortFlags = 0x0 + , kMethcla_Trigger = 0x1 +} Methcla_PortFlags; + +typedef struct Methcla_PortDescriptor Methcla_PortDescriptor; + +struct Methcla_PortDescriptor +{ + Methcla_PortDirection direction; + Methcla_PortType type; + Methcla_PortFlags flags; +}; + +typedef uint16_t Methcla_PortCount; + +typedef void Methcla_SynthOptions; + +typedef struct Methcla_SynthDef Methcla_SynthDef; + +struct Methcla_SynthDef +{ + //* Synth definition URI. + const char* uri; + + //* Size of an instance in bytes. + size_t instance_size; + + //* Size of options struct in bytes. + size_t options_size; + + //* Parse OSC options and fill options struct. + void (*configure)(const void* tag_buffer, size_t tag_size, const void* arg_buffer, size_t arg_size, Methcla_SynthOptions* options); + + //* Get port descriptor at index. + bool (*port_descriptor)(const Methcla_SynthOptions* options, Methcla_PortCount index, Methcla_PortDescriptor* port); + + //* Construct a synth instance at the location given. + void (*construct)(const Methcla_World* world, const Methcla_SynthDef* def, const Methcla_SynthOptions* options, Methcla_Synth* synth); + + //* Connect port at index to data. + void (*connect)(Methcla_Synth* synth, Methcla_PortCount index, void* data); + + //* Activate the synth instance just before starting to call `process`. + void (*activate)(const Methcla_World* world, Methcla_Synth* synth); + + //* Process numFrames of audio samples. + void (*process)(const Methcla_World* world, Methcla_Synth* synth, size_t numFrames); + + //* Destroy a synth instance. + void (*destroy)(const Methcla_World* world, Methcla_Synth* synth); +}; + +struct Methcla_Host +{ + //* Handle for implementation specific data. + void* handle; + + //* Register a synth definition. + void (*register_synthdef)(const struct Methcla_Host* host, const Methcla_SynthDef* synthDef); + + //* Register sound file API. + void (*register_soundfile_api)(const struct Methcla_Host* host, const Methcla_SoundFileAPI* api); + + //* Allocate a block of memory + void* (*alloc)(const struct Methcla_Host* context, size_t size); + + //* Allocate a block of aligned memory. + void* (*alloc_aligned)(const struct Methcla_Host* context, size_t alignment, size_t size); + + //* Free a block of memory previously allocated by alloc or alloc_aligned. + void (*free)(const struct Methcla_Host* context, void* ptr); + + //* Open sound file. + Methcla_Error (*soundfile_open)(const Methcla_Host* host, const char* path, Methcla_FileMode mode, Methcla_SoundFile** file, Methcla_SoundFileInfo* info); + + //* Schedule a command for execution in the realtime context. + void (*perform_command)(const Methcla_Host* host, const Methcla_WorldPerformFunction perform, void* data); + + //* Send an OSC notification packet to the client. + void (*notify)(const Methcla_Host* host, const void* packet, size_t size); + + //* Log a message and a newline character. + void (*log_line)(const Methcla_Host* host, Methcla_LogLevel level, const char* message); +}; + +static inline void methcla_host_register_synthdef(const Methcla_Host* host, const Methcla_SynthDef* synthDef) +{ + assert(host && host->register_synthdef); + assert(synthDef); + host->register_synthdef(host, synthDef); +} + +static inline void methcla_host_register_soundfile_api(const Methcla_Host* host, const Methcla_SoundFileAPI* api) +{ + assert(host && host->register_soundfile_api && api); + host->register_soundfile_api(host, api); +} + +static inline void* methcla_host_alloc(const Methcla_Host* context, size_t size) +{ + assert(context); + assert(context->alloc); + return context->alloc(context, size); +} + +static inline void* methcla_host_alloc_aligned(const Methcla_Host* context, size_t alignment, size_t size) +{ + assert(context); + assert(context->alloc_aligned); + return context->alloc_aligned(context, alignment, size); +} + +static inline void methcla_host_free(const Methcla_Host* context, void* ptr) +{ + assert(context); + assert(context->free); + context->free(context, ptr); +} + +static inline Methcla_Error methcla_host_soundfile_open(const Methcla_Host* host, const char* path, Methcla_FileMode mode, Methcla_SoundFile** file, Methcla_SoundFileInfo* info) +{ + assert(host && host->soundfile_open); + assert(path); + assert(file); + assert(info); + return host->soundfile_open(host, path, mode, file, info); +} + +static inline void methcla_host_perform_command(const Methcla_Host* host, Methcla_WorldPerformFunction perform, void* data) +{ + assert(host && host->perform_command); + host->perform_command(host, perform, data); +} + +static inline void methcla_host_log_line(const Methcla_Host* host, Methcla_LogLevel level, const char* message) +{ + assert(host); + assert(host->log_line); + assert(message); + host->log_line(host, level, message); +} + +typedef struct Methcla_Library Methcla_Library; + +struct Methcla_Library +{ + //* Handle for implementation specific data. + void* handle; + + //* Destroy the library and clean up associated resources. + void (*destroy)(const Methcla_Library* library); +}; + +typedef const Methcla_Library* (*Methcla_LibraryFunction)(const Methcla_Host* host, const char* bundlePath); + +static inline void methcla_library_destroy(const Methcla_Library* library) +{ + assert(library); + if (library->destroy) + library->destroy(library); +} + +// #define MESCALINE_MAKE_INIT_FUNC(name) MethclaInit_##name +// #define MESCALINE_INIT_FUNC(name) MESCALINE_MAKE_INIT_FUNC(name) + +#if defined(__cplusplus) +} +#endif + +#endif /* METHCLA_PLUGIN_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugin.hpp b/java/libraries/sound/src/cpp/include/methcla/plugin.hpp new file mode 100644 index 000000000..219c3f5d6 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugin.hpp @@ -0,0 +1,305 @@ +// Copyright 2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_PLUGIN_HPP_INCLUDED +#define METHCLA_PLUGIN_HPP_INCLUDED + +#include +#include +#include + +#include +#include + +// NOTE: This API is unstable and subject to change! + +namespace Methcla { namespace Plugin { + + template class World + { + const Methcla_World* m_context; + + public: + World(const Methcla_World* context) + : m_context(context) + { } + + double sampleRate() const + { + return methcla_world_samplerate(m_context); + } + + size_t blockSize() const + { + return methcla_world_block_size(m_context); + } + + Methcla_Time currentTime() const + { + return methcla_world_current_time(m_context); + } + + void* alloc(size_t size) const + { + return methcla_world_alloc(m_context, size); + } + + void* allocAligned(size_t alignment, size_t size) const + { + return methcla_world_alloc_aligned(m_context, alignment, size); + } + + void free(void* ptr) + { + methcla_world_free(m_context, ptr); + } + + void performCommand(Methcla_HostPerformFunction perform, void* data) + { + methcla_world_perform_command(m_context, perform, data); + } + + LogStream log(Methcla_LogLevel logLevel=kMethcla_LogInfo) + { + using namespace std::placeholders; + return LogStream(std::bind(m_context->log_line, m_context, _1, _2), logLevel); + } + + void synthRetain(Synth* synth) const + { + methcla_world_synth_retain(m_context, synth); + } + + void synthRelease(Synth* synth) const + { + methcla_world_synth_release(m_context, synth); + } + + void synthDone(Synth* synth) const + { + methcla_world_synth_done(m_context, synth); + } + }; + + class HostContext + { + const Methcla_Host* m_context; + + public: + HostContext(const Methcla_Host* context) + : m_context(context) + {} + + LogStream log(Methcla_LogLevel logLevel=kMethcla_LogInfo) + { + using namespace std::placeholders; + return LogStream(std::bind(m_context->log_line, m_context, _1, _2), logLevel); + } + }; + + class NoPorts + { + public: + enum Port { }; + + static size_t numPorts() { return 0; } + + static Methcla_PortDescriptor descriptor(Port) + { + Methcla_PortDescriptor result; + std::memset(&result, 0, sizeof(result)); + return result; + } + }; + + class PortDescriptor + { + public: + static Methcla_PortDescriptor make(Methcla_PortDirection direction, Methcla_PortType type, Methcla_PortFlags flags=kMethcla_PortFlags) + { + Methcla_PortDescriptor pd; + pd.direction = direction; + pd.type = type; + pd.flags = flags; + return pd; + } + + static Methcla_PortDescriptor audioInput(Methcla_PortFlags flags=kMethcla_PortFlags) + { + return make(kMethcla_Input, kMethcla_AudioPort, flags); + } + + static Methcla_PortDescriptor audioOutput(Methcla_PortFlags flags=kMethcla_PortFlags) + { + return make(kMethcla_Output, kMethcla_AudioPort, flags); + } + + static Methcla_PortDescriptor controlInput(Methcla_PortFlags flags=kMethcla_PortFlags) + { + return make(kMethcla_Input, kMethcla_ControlPort, flags); + } + + static Methcla_PortDescriptor controlOutput(Methcla_PortFlags flags=kMethcla_PortFlags) + { + return make(kMethcla_Output, kMethcla_ControlPort, flags); + } + }; + + template class StaticSynthOptions + { + public: + typedef Options Type; + + static void + configure( const void* tag_buffer + , size_t tag_buffer_size + , const void* arg_buffer + , size_t arg_buffer_size + , Methcla_SynthOptions* options ) + { + OSCPP::Server::ArgStream args( + OSCPP::ReadStream(tag_buffer, tag_buffer_size), + OSCPP::ReadStream(arg_buffer, arg_buffer_size) + ); + new (options) Type(args); + } + + static bool + port_descriptor( const Methcla_SynthOptions* + , Methcla_PortCount index + , Methcla_PortDescriptor* port ) + { + if (index < PortDescriptor::numPorts()) + { + *port = PortDescriptor::descriptor(static_cast(index)); + return true; + } + return false; + } + }; + + namespace detail + { + template + class IfSynthDefHasActivate + { + public: + static inline void exec(const Methcla_World*, Synth*) { } + }; + + template + class IfSynthDefHasActivate + { + public: + static inline void exec(const Methcla_World* context, Synth* synth) + { synth->activate(World(context)); } + }; + + template + class IfSynthDefHasCleanup + { + public: + static inline void exec(const Methcla_World*, Synth*) { } + }; + + template + class IfSynthDefHasCleanup + { + public: + static inline void exec(const Methcla_World* context, Synth* synth) + { synth->cleanup(World(context)); } + }; + } // namespace detail + + enum SynthDefFlags + { + kSynthDefDefaultFlags = 0x00, + kSynthDefHasActivate = 0x01, + kSynthDefHasCleanup = 0x02 + }; + + template class SynthDef + { + static void + construct( const Methcla_World* context + , const Methcla_SynthDef* synthDef + , const Methcla_SynthOptions* options + , Methcla_Synth* synth ) + { + assert(context != nullptr); + assert(options != nullptr); + new (synth) Synth(World(context), synthDef, *static_cast(options)); + } + + static void + connect( Methcla_Synth* synth + , Methcla_PortCount port + , void* data) + { + static_cast(synth)->connect(static_cast(port), data); + } + + static void + activate(const Methcla_World* context, Methcla_Synth* synth) + { + detail::IfSynthDefHasActivate< + Synth, + (Flags & kSynthDefHasActivate) == kSynthDefHasActivate + >::exec(context, static_cast(synth)); + } + + static void + process(const Methcla_World* context, Methcla_Synth* synth, size_t numFrames) + { + static_cast(synth)->process(World(context), numFrames); + } + + static void + destroy(const Methcla_World* context, Methcla_Synth* synth) + { + // Call cleanup method + detail::IfSynthDefHasActivate< + Synth, + (Flags & kSynthDefHasCleanup) == kSynthDefHasCleanup + >::exec(context, static_cast(synth)); + // Call destructor + static_cast(synth)->~Synth(); + } + + public: + void operator()(const Methcla_Host* host, const char* uri) + { + static const Methcla_SynthDef kSynthDef = + { + uri, + sizeof(Synth), + sizeof(typename Options::Type), + Options::configure, + Options::port_descriptor, + construct, + connect, + activate, + process, + destroy + }; + methcla_host_register_synthdef(host, &kSynthDef); + } + }; + + template + using StaticSynthDef + = SynthDef, Ports, Flags>; +} } + +#endif // METHCLA_PLUGIN_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/ampfol.h b/java/libraries/sound/src/cpp/include/methcla/plugins/ampfol.h new file mode 100644 index 000000000..8d4823326 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/ampfol.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_AMPLITUDE_FOLLOWER_H_INCLUDED +#define METHCLA_PLUGINS_AMPLITUDE_FOLLOWER_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_amplitude_follower(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_AMPLITUDE_FOLLOWER_URI METHCLA_PLUGINS_URI "/amplidute_follower" + +#endif /* METHCLA_PLUGINS_AMPLITUDE_FOLLOWER_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/delay.h b/java/libraries/sound/src/cpp/include/methcla/plugins/delay.h new file mode 100644 index 000000000..36ffee64a --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/delay.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_DELAY_H_INCLUDED +#define METHCLA_PLUGINS_DELAY_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_delay(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_DELAY_URI METHCLA_PLUGINS_URI "/delay" + +#endif /* METHCLA_PLUGINS_DELAY_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/fft.h b/java/libraries/sound/src/cpp/include/methcla/plugins/fft.h new file mode 100644 index 000000000..7ef3fde5b --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/fft.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_FFT_H_INCLUDED +#define METHCLA_PLUGINS_FFT_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_fft(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_FFT_URI METHCLA_PLUGINS_URI "/fft" + +#endif /* METHCLA_PLUGINS_FFT_H_INCLUDED */ \ No newline at end of file diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/hpf.h b/java/libraries/sound/src/cpp/include/methcla/plugins/hpf.h new file mode 100644 index 000000000..a9d0dcc43 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/hpf.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_HPF_H_INCLUDED +#define METHCLA_PLUGINS_HPF_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_hpf(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_HPF_URI METHCLA_PLUGINS_URI "/hpf" + +#endif /* METHCLA_PLUGINS_HPF_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/lpf.h b/java/libraries/sound/src/cpp/include/methcla/plugins/lpf.h new file mode 100644 index 000000000..8dd48df45 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/lpf.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_LPF_H_INCLUDED +#define METHCLA_PLUGINS_LPF_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_lpf(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_LPF_URI METHCLA_PLUGINS_URI "/lpf" + +#endif /* METHCLA_PLUGINS_LPF_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/node-control.h b/java/libraries/sound/src/cpp/include/methcla/plugins/node-control.h new file mode 100644 index 000000000..300389093 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/node-control.h @@ -0,0 +1,27 @@ +/* + Copyright 2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_NODE_CONTROL_H_INCLUDED +#define METHCLA_PLUGINS_NODE_CONTROL_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_node_control(const Methcla_Host*, const char*); + +#define METHCLA_PLUGINS_DONE_AFTER_URI METHCLA_PLUGINS_URI "/done-after" +#define METHCLA_PLUGINS_ASR_ENVELOPE_URI METHCLA_PLUGINS_URI "/asr-envelope" + +#endif // METHCLA_PLUGINS_NODE_CONTROL_H_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/osc.h b/java/libraries/sound/src/cpp/include/methcla/plugins/osc.h new file mode 100644 index 000000000..2449e082b --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/osc.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_OSC_H_INCLUDED +#define METHCLA_PLUGINS_OSC_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_osc(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_OSC_URI METHCLA_PLUGINS_URI "/osc" + +#endif /* METHCLA_PLUGINS_OSC_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/pan.h b/java/libraries/sound/src/cpp/include/methcla/plugins/pan.h new file mode 100644 index 000000000..5a1a7e468 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/pan.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_PAN_H_INCLUDED +#define METHCLA_PLUGINS_PAN_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_pan(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_PAN_URI METHCLA_PLUGINS_URI "/pan" + +#endif /* METHCLA_PLUGINS_PAN_H_INCLUDED */ \ No newline at end of file diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/pan2.h b/java/libraries/sound/src/cpp/include/methcla/plugins/pan2.h new file mode 100644 index 000000000..e1480ab7d --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/pan2.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_PAN2_H_INCLUDED +#define METHCLA_PLUGINS_PAN2_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_pan2(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_PAN2_URI METHCLA_PLUGINS_URI "/pan2" + +#endif /* METHCLA_PLUGINS_PAN2_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/patch-cable.h b/java/libraries/sound/src/cpp/include/methcla/plugins/patch-cable.h new file mode 100644 index 000000000..1c416bd23 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/patch-cable.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_PATCH_CABLE_H_INCLUDED +#define METHCLA_PLUGINS_PATCH_CABLE_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_patch_cable(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_PATCH_CABLE_URI METHCLA_PLUGINS_URI "/patch-cable" + +#endif // METHCLA_PLUGINS_PATCH_CABLE_H_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/pro/disksampler.h b/java/libraries/sound/src/cpp/include/methcla/plugins/pro/disksampler.h new file mode 100644 index 000000000..538065718 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/pro/disksampler.h @@ -0,0 +1,15 @@ +// Copyright 2012-2013 Samplecount S.L. +// All Rights Reserved. +// +// See the file LICENSE-PRO for licensing details. + +#ifndef METHCLA_PLUGINS_DISKSAMPLER_H_INCLUDED +#define METHCLA_PLUGINS_DISKSAMPLER_H_INCLUDED + +#include + +#define METHCLA_PLUGINS_DISKSAMPLER "methcla_plugins_disksampler" +METHCLA_EXPORT const Methcla_Library* methcla_plugins_disksampler(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_DISKSAMPLER_URI METHCLA_PLUGINS_URI "/disksampler" + +#endif // METHCLA_PLUGINS_DISKSAMPLER_H_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/pro/soundfile_api_extaudiofile.h b/java/libraries/sound/src/cpp/include/methcla/plugins/pro/soundfile_api_extaudiofile.h new file mode 100644 index 000000000..1f3541737 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/pro/soundfile_api_extaudiofile.h @@ -0,0 +1,14 @@ +// Copyright 2012-2013 Samplecount S.L. +// All Rights Reserved. +// +// See the file LICENSE-PRO for licensing details. + +#ifndef METHCLA_SOUNDFILEAPI_EXTAUDIOFILE_H_INCLUDED +#define METHCLA_SOUNDFILEAPI_EXTAUDIOFILE_H_INCLUDED + +#include +#include + +METHCLA_EXPORT const Methcla_Library* methcla_soundfile_api_extaudiofile(const Methcla_Host*, const char*); + +#endif // METHCLA_SOUNDFILEAPI_EXTAUDIOFILE_H_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/pulse.h b/java/libraries/sound/src/cpp/include/methcla/plugins/pulse.h new file mode 100644 index 000000000..0624df72d --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/pulse.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_PULSE_H_INCLUDED +#define METHCLA_PLUGINS_PULSE_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_pulse(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_PULSE_URI METHCLA_PLUGINS_URI "/pulse" + +#endif /* METHCLA_PLUGINS_PULSE_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/sampler.h b/java/libraries/sound/src/cpp/include/methcla/plugins/sampler.h new file mode 100644 index 000000000..4c2f666dd --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/sampler.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_SAMPLER_H_INCLUDED +#define METHCLA_PLUGINS_SAMPLER_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_sampler(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_SAMPLER_URI METHCLA_PLUGINS_URI "/sampler" + +#endif /* METHCLA_PLUGINS_SAMPLER_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/saw.h b/java/libraries/sound/src/cpp/include/methcla/plugins/saw.h new file mode 100644 index 000000000..0650798e0 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/saw.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_SAW_H_INCLUDED +#define METHCLA_PLUGINS_SAW_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_saw(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_SAW_URI METHCLA_PLUGINS_URI "/saw" + +#endif /* METHCLA_PLUGINS_SAW_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/sine.h b/java/libraries/sound/src/cpp/include/methcla/plugins/sine.h new file mode 100644 index 000000000..d40a4a207 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/sine.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_SINE_H_INCLUDED +#define METHCLA_PLUGINS_SINE_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_sine(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_SINE_URI METHCLA_PLUGINS_URI "/sine" + +#endif /* METHCLA_PLUGINS_SINE_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_dummy.h b/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_dummy.h new file mode 100644 index 000000000..3eb39f43d --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_dummy.h @@ -0,0 +1,23 @@ +// Copyright 2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_SOUNDFILEAPI_DUMMY_H_INCLUDED +#define METHCLA_SOUNDFILEAPI_DUMMY_H_INCLUDED + +#include +#include + +METHCLA_EXPORT const Methcla_Library* methcla_soundfile_api_dummy(const Methcla_Host*, const char*); + +#endif /* METHCLA_SOUNDFILEAPI_DUMMY_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_libsndfile.h b/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_libsndfile.h new file mode 100644 index 000000000..0f908c711 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_libsndfile.h @@ -0,0 +1,23 @@ +// Copyright 2013 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_SOUNDFILEAPI_LIBSNDFILE_H_INCLUDED +#define METHCLA_SOUNDFILEAPI_LIBSNDFILE_H_INCLUDED + +#include +#include + +METHCLA_EXPORT const Methcla_Library* methcla_soundfile_api_libsndfile(const Methcla_Host*, const char*); + +#endif /* METHCLA_SOUNDFILEAPI_LIBSNDFILE_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_mpg123.h b/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_mpg123.h new file mode 100644 index 000000000..d18d143a8 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/soundfile_api_mpg123.h @@ -0,0 +1,23 @@ +// Copyright 2013-2014 Samplecount S.L. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef METHCLA_SOUNDFILEAPI_MPG123_H_INCLUDED +#define METHCLA_SOUNDFILEAPI_MPG123_H_INCLUDED + +#include +#include + +METHCLA_EXPORT const Methcla_Library* methcla_soundfile_api_mpg123(const Methcla_Host*, const char*); + +#endif /* METHCLA_SOUNDFILEAPI_MPG123_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/tri.h b/java/libraries/sound/src/cpp/include/methcla/plugins/tri.h new file mode 100644 index 000000000..0daa3b3b8 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/tri.h @@ -0,0 +1,25 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_PLUGINS_TRI_H_INCLUDED +#define METHCLA_PLUGINS_TRI_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_tri(const Methcla_Host*, const char*); +#define METHCLA_PLUGINS_TRI_URI METHCLA_PLUGINS_URI "/tri" + +#endif /* METHCLA_PLUGINS_TRI_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/methcla/plugins/whitenoise.h b/java/libraries/sound/src/cpp/include/methcla/plugins/whitenoise.h new file mode 100644 index 000000000..535f57482 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/plugins/whitenoise.h @@ -0,0 +1,19 @@ +// +// whitenoise.h +// +// +// Created by wirsing on 13.12.13. +// +// + + +#ifndef METHCLA_PLUGINS_WHITE_NOISE_H_INCLUDED +#define METHCLA_PLUGINS_WHITE_NOISE_H_INCLUDED + +#include + +METHCLA_EXPORT const Methcla_Library* methcla_plugins_white_noise(const Methcla_Host*, const char*); + +#define METHCLA_PLUGINS_WHITE_NOISE_URI METHCLA_PLUGINS_URI "/white_noise" + +#endif // METHCLA_PLUGINS_WHITE_NOISE_H_INCLUDED \ No newline at end of file diff --git a/java/libraries/sound/src/cpp/include/methcla/types.h b/java/libraries/sound/src/cpp/include/methcla/types.h new file mode 100644 index 000000000..728a3cc36 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/methcla/types.h @@ -0,0 +1,46 @@ +/* + Copyright 2012-2013 Samplecount S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef METHCLA_TYPES_H_INCLUDED +#define METHCLA_TYPES_H_INCLUDED + +enum Methcla_NodePlacement +{ + kMethcla_NodePlacementHeadOfGroup, + kMethcla_NodePlacementTailOfGroup, + kMethcla_NodePlacementBeforeNode, + kMethcla_NodePlacementAfterNode +}; + +enum Methcla_BusMappingFlags +{ + kMethcla_BusMappingInternal = 0x00 + , kMethcla_BusMappingExternal = 0x01 + , kMethcla_BusMappingFeedback = 0x02 + , kMethcla_BusMappingReplace = 0x04 +}; + +enum Methcla_NodeDoneFlags +{ + kMethcla_NodeDoneDoNothing = 0x00 + , kMethcla_NodeDoneFreeSelf = 0x01 + , kMethcla_NodeDoneFreePreceeding = 0x02 + , kMethcla_NodeDoneFreeFollowing = 0x04 + , kMethcla_NodeDoneFreeAllSiblings = 0x08 + , kMethcla_NodeDoneFreeParent = 0x10 +}; + +#endif /* METHCLA_TYPES_H_INCLUDED */ diff --git a/java/libraries/sound/src/cpp/include/oscpp/client.hpp b/java/libraries/sound/src/cpp/include/oscpp/client.hpp new file mode 100644 index 000000000..04b4c3315 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/client.hpp @@ -0,0 +1,314 @@ +// oscpp library +// +// Copyright (c) 2004-2013 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_CLIENT_HPP_INCLUDED +#define OSCPP_CLIENT_HPP_INCLUDED + +#include +#include +#include + +#include +#include +#include +#include + +namespace OSCPP { +namespace Client { + +//! OSC packet construction. +/*! + * Construct a valid OSC packet for transmitting over a transport medium. + */ +class Packet +{ + int32_t calcSize(const char* begin, const char* end) + { + // TODO: Make sure pointer difference fits into int32_t + return end - begin - 4; + } + +public: + //! Constructor. + /*! + */ + Packet() + { + reset(0, 0); + } + + //! Constructor. + /*! + */ + Packet(void* buffer, size_t size) + { + reset(buffer, size); + } + + //! Destructor. + virtual ~Packet() { } + + //! Get packet buffer address. + /*! + * Return the start address of the packet currently under construction. + */ + void* data() const + { + return m_buffer; + } + + size_t capacity() const + { + return m_capacity; + } + + //! Get packet content size. + /*! + * Return the size of the packet currently under construction. + */ + size_t size() const + { + return m_args.consumed(); + } + + //! Reset packet state. + void reset(void* buffer, size_t size) + { + checkAlignment(&m_buffer, kAlignment); + m_buffer = buffer; + m_capacity = size; + m_args = WriteStream(m_buffer, m_capacity); + m_sizePosM = m_sizePosB = nullptr; + m_inBundle = 0; + } + + void reset() + { + reset(m_buffer, m_capacity); + } + + Packet& openBundle(uint64_t time) + { + if (m_inBundle > 0) { + // Remember previous size pos offset + // TODO: Make sure pointer difference fits into int32_t + const int32_t offset = m_sizePosB - m_args.begin(); + char* curPos = m_args.pos(); + m_args.skip(4); + // Record size pos + std::memcpy(curPos, &offset, 4); + m_sizePosB = curPos; + } else if (m_args.pos() != m_args.begin()) { + throw std::logic_error("Cannot open toplevel bundle in non-empty packet"); + } + + m_inBundle++; + m_args.putString("#bundle"); + m_args.putUInt64(time); + return *this; + } + + Packet& closeBundle() + { + if (m_inBundle > 0) { + if (m_inBundle > 1) { + // Get current stream pos + char* curPos = m_args.pos(); + + // Get previous bundle size stream pos + int32_t offset; + memcpy(&offset, m_sizePosB, 4); + // Get previous size pos + char* prevPos = m_args.begin() + offset; + + const int32_t bundleSize = calcSize(m_sizePosB, curPos); + assert(bundleSize >= 0 && (size_t)bundleSize >= Size::bundle(0)); + // Write bundle size + m_args.setPos(m_sizePosB); + m_args.putInt32(bundleSize); + m_args.setPos(curPos); + + // record outer bundle size pos + m_sizePosB = prevPos; + } + m_inBundle--; + } else { + throw std::logic_error("closeBundle() without matching openBundle()"); + } + return *this; + } + + Packet& openMessage(const char* addr, size_t numTags) + { + if (m_inBundle > 0) { + // record message size pos + m_sizePosM = m_args.pos(); + // advance arg stream + m_args.skip(4); + } + m_args.putString(addr); + size_t sigLen = numTags + 2; + m_tags = WriteStream(m_args, sigLen); + m_args.zero(align(sigLen)); + m_tags.putChar(','); + return *this; + } + + Packet& closeMessage() + { + if (m_inBundle > 0) { + // Get current stream pos + char* curPos = m_args.pos(); + // write message size + m_args.setPos(m_sizePosM); + m_args.putInt32(calcSize(m_sizePosM, curPos)); + // restore stream pos + m_args.setPos(curPos); + // reset tag stream + m_tags = WriteStream(); + } + return *this; + } + + //! Write integer message argument. + /*! + * Write a 32 bit integer message argument. + * + * \param arg 32 bit integer argument. + * + * \pre openMessage must have been called before with no intervening + * closeMessage. + * + * \throw OSCPP::XRunError stream buffer xrun. + */ + Packet& int32(int32_t arg) + { + m_tags.putChar('i'); + m_args.putInt32(arg); + return *this; + } + + Packet& float32(float arg) + { + m_tags.putChar('f'); + m_args.putFloat32(arg); + return *this; + } + + Packet& string(const char* arg) + { + m_tags.putChar('s'); + m_args.putString(arg); + return *this; + } + + // @throw std::invalid_argument if blob size is greater than std::numeric_limits::max() + Packet& blob(const Blob& arg) + { + if (arg.size() > (size_t)std::numeric_limits::max()) + throw std::invalid_argument("Blob size greater than maximum value representable by int32_t"); + m_tags.putChar('b'); + m_args.putInt32(arg.size()); + m_args.putData(arg.data(), arg.size()); + return *this; + } + + Packet& openArray() + { + m_tags.putChar('['); + return *this; + } + + Packet& closeArray() + { + m_tags.putChar(']'); + return *this; + } + + template Packet& put(T) + { + T::OSC_Client_Packet_put_unimplemented; + return *this; + } + + template Packet& put(InputIterator begin, InputIterator end) + { + for (auto it = begin; it != end; it++) { + put(*it); + } + return *this; + } + + template Packet& putArray(InputIterator begin, InputIterator end) + { + openArray(); + put(begin, end); + closeArray(); + return *this; + } + +private: + void* m_buffer; + size_t m_capacity; + WriteStream m_args; // packet stream + WriteStream m_tags; // current tag stream + char* m_sizePosM; // last message size position + char* m_sizePosB; // last bundle size position + size_t m_inBundle; // bundle nesting depth +}; + +template <> inline Packet& Packet::put(int32_t x) { return int32(x); } +template <> inline Packet& Packet::put(float x) { return float32(x); } +template <> inline Packet& Packet::put(const char* x) { return string(x); } +template <> inline Packet& Packet::put(Blob x) { return blob(x); } + +template class StaticPacket : public Packet +{ +public: + StaticPacket() + : Packet(reinterpret_cast(&m_buffer), buffer_size) + { } + +private: + typedef typename std::aligned_storage::type AlignedBuffer; + AlignedBuffer m_buffer; +}; + +class DynamicPacket : public Packet +{ +public: + DynamicPacket(size_t buffer_size) + : Packet(static_cast(new char[buffer_size]), buffer_size) + { } + + ~DynamicPacket() + { + delete [] static_cast(data()); + } +}; + +} +} + +#endif // OSCPP_CLIENT_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/detail/endian.hpp b/java/libraries/sound/src/cpp/include/oscpp/detail/endian.hpp new file mode 100644 index 000000000..11bb76727 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/detail/endian.hpp @@ -0,0 +1,85 @@ +// Copyright 2005 Caleb Epstein +// Copyright 2006 John Maddock +// Copyright 2010 Rene Rivera +// Distributed under the Boost Software License, Version 1.0. (See accompany- +// ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/* + * Copyright (c) 1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +/* + * Copyright notice reproduced from , from + * which this code was originally taken. + * + * Modified by Caleb Epstein to use with GNU libc and to + * defined the BOOST_ENDIAN macro. + */ + +/* + * Modifications for oscpp by Stefan Kersten + * - Change prefix from BOOST to OSCPP + * - Remove PDP endianness + * - Add OSCPP_BYTE_ORDER_* macros + */ + +#ifndef OSCPP_ENDIAN_HPP_INCLUDED +#define OSCPP_ENDIAN_HPP_INCLUDED + +#define OSCPP_BYTE_ORDER_BIG_ENDIAN 4321 +#define OSCPP_BYTE_ORDER_LITTLE_ENDIAN 1234 + +// GNU libc offers the helpful header which defines +// __BYTE_ORDER + +#if defined (__GLIBC__) || defined(__ANDROID__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define OSCPP_LITTLE_ENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define OSCPP_BIG_ENDIAN +# else +# error Unknown machine endianness detected. +# endif +# define OSCPP_BYTE_ORDER __BYTE_ORDER +#elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) || \ + defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) || \ + defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN) +# define OSCPP_BIG_ENDIAN +# define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_BIG_ENDIAN +#elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) || \ + defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) || \ + defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN) +# define OSCPP_LITTLE_ENDIAN +# define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_LITTLE_ENDIAN +#elif defined(__sparc) || defined(__sparc__) \ + || defined(_POWER) || defined(__powerpc__) \ + || defined(__ppc__) || defined(__hpux) || defined(__hppa) \ + || defined(_MIPSEB) || defined(_POWER) \ + || defined(__s390__) +# define OSCPP_BIG_ENDIAN +# define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_BIG_ENDIAN +#elif defined(__i386__) || defined(__alpha__) \ + || defined(__ia64) || defined(__ia64__) \ + || defined(_M_IX86) || defined(_M_IA64) \ + || defined(_M_ALPHA) || defined(__amd64) \ + || defined(__amd64__) || defined(_M_AMD64) \ + || defined(__x86_64) || defined(__x86_64__) \ + || defined(_M_X64) || defined(__bfin__) + +# define OSCPP_LITTLE_ENDIAN +# define OSCPP_BYTE_ORDER OSCPP_BYTE_ORDER_LITTLE_ENDIAN +#else +# error The file oscpp/endian.hpp needs to be set up for your CPU type. +#endif + +#endif // OSCPP_ENDIAN_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/detail/host.hpp b/java/libraries/sound/src/cpp/include/oscpp/detail/host.hpp new file mode 100644 index 000000000..ec52fd0b5 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/detail/host.hpp @@ -0,0 +1,118 @@ +// oscpp library +// +// Copyright (c) 2004-2013 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_HOST_HPP_INCLUDED +#define OSCPP_HOST_HPP_INCLUDED + +#include +#include +#include + +namespace OSCPP +{ +#if defined(__GNUC__) + inline static uint32_t bswap32(uint32_t x) + { + return __builtin_bswap32(x); + } + inline static uint64_t bswap64(uint64_t x) + { + return __builtin_bswap64(x); + } +#elif defined(_WINDOWS_) +# include + inline static uint32_t bswap32(uint32_t x) + { + return _byteswap_ulong(x); + } + inline static uint64_t bswap64(uint64_t x) + { + return _byteswap_uint64(x); + } +#else + // Fallback implementation +# warning Using unoptimized byte swap functions + + inline static uint32_t bswap32(uint32_t x) + { + const uint32_t b1 = x << 24; + const uint32_t b2 = (x & 0x0000FF00) << 8; + const uint32_t b3 = (x & 0x00FF0000) >> 8; + const uint32_t b4 = x >> 24; + return b1 | b2 | b3 | b4; + } + inline static uint64_t bswap64(int64_t x) + { + const uint64_t w1 = oscpp_bswap(uint32_t(x & 0x00000000FFFFFFFF)) << 32; + const uint64_t w2 = oscpp_bswap(uint32_t(x >> 32)); + return w1 | w2; + } +#endif + + enum ByteOrder + { + NetworkByteOrder, + HostByteOrder + }; + + template inline uint32_t convert32(uint32_t) + { + throw std::logic_error("Invalid byte order"); + } + + template<> inline uint32_t convert32(uint32_t x) + { +#if defined(OSCPP_LITTLE_ENDIAN) + return bswap32(x); +#else + return x; +#endif + } + + template<> inline uint32_t convert32(uint32_t x) + { + return x; + } + + template inline uint64_t convert64(uint64_t) + { + throw std::logic_error("Invalid byte order"); + } + + template<> inline uint64_t convert64(uint64_t x) + { +#if defined(OSCPP_LITTLE_ENDIAN) + return bswap64(x); +#else + return x; +#endif + } + + template<> inline uint64_t convert64(uint64_t x) + { + return x; + } +} + +#endif // OSCPP_HOST_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/detail/stream.hpp b/java/libraries/sound/src/cpp/include/oscpp/detail/stream.hpp new file mode 100644 index 000000000..41520290a --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/detail/stream.hpp @@ -0,0 +1,346 @@ +// oscpp library +// +// Copyright (c) 2004-2013 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_STREAM_HPP_INCLUDED +#define OSCPP_STREAM_HPP_INCLUDED + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace OSCPP { + +class Stream +{ +public: + Stream() + { + m_begin = m_end = m_pos = 0; + } + + Stream(void* data, size_t size) + { + m_begin = static_cast(data); + m_end = m_begin + size; + m_pos = m_begin; + } + + Stream(const Stream& stream) + { + m_begin = m_pos = stream.m_pos; + m_end = stream.m_end; + } + + Stream(const Stream& stream, size_t size) + { + m_begin = m_pos = stream.m_pos; + m_end = m_begin + size; + if (m_end > stream.m_end) throw UnderrunError(); + } + + void reset() + { + m_pos = m_begin; + } + + const char* begin() const + { + return m_begin; + } + + char* begin() + { + return m_begin; + } + + const char* end() const + { + return m_end; + } + + size_t capacity() const + { + return end() - begin(); + } + + const char* pos() const + { + return m_pos; + } + + char* pos() + { + return m_pos; + } + + void setPos(char* pos) + { + assert((pos >= m_begin) && (pos <= m_end)); + m_pos = pos; + } + + void advance(size_t n) + { + m_pos += n; + } + + bool atEnd() const + { + return pos() == end(); + } + + size_t consumed() const + { + return pos() - begin(); + } + + size_t consumable() const + { + return end() - pos(); + } + + inline void checkAlignment(size_t n) const + { + OSCPP::checkAlignment(pos(), n); + } + +protected: + char* m_begin; + char* m_end; + char* m_pos; +}; + +class WriteStream: public Stream +{ +public: + WriteStream() + : Stream() + { } + + WriteStream(void* data, size_t size) + : Stream(data, size) + { } + + WriteStream(const WriteStream& stream) + : Stream(stream) + { } + + WriteStream(const WriteStream& stream, size_t size) + : Stream(stream, size) + { } + + // throw (OverflowError) + inline void checkWritable(size_t n) const + { + if (consumable() < n) + throw OverflowError(n - consumable()); + } + + void skip(size_t n) + { + checkWritable(n); + advance(n); + } + + void zero(size_t n) + { + checkWritable(n); + std::memset(m_pos, 0, n); + advance(n); + } + + void putChar(char c) + { + checkWritable(1); + *pos() = c; + advance(1); + } + + void putInt32(int32_t x) + { + checkWritable(4); + checkAlignment(4); + uint32_t uh; + memcpy(&uh, &x, 4); + const uint32_t un = convert32(uh); + std::memcpy(pos(), &un, 4); + advance(4); + } + + void putUInt64(uint64_t x) + { + checkWritable(8); + const uint64_t un = convert64(x); + std::memcpy(pos(), &un, 8); + advance(8); + } + + void putFloat32(float f) + { + checkWritable(4); + checkAlignment(4); + uint32_t uh; + std::memcpy(&uh, &f, 4); + const uint32_t un = convert32(uh); + std::memcpy(pos(), &un, 4); + advance(4); + } + + void putData(const void* data, size_t size) + { + const size_t padding = OSCPP::padding(size); + const size_t n = size + padding; + checkWritable(n); + std::memcpy(pos(), data, size); + std::memset(pos()+size, 0, padding); + advance(n); + } + + void putString(const char* s) + { + putData(s, strlen(s)+1); + } +}; + + +class ReadStream : public Stream +{ +public: + ReadStream() + { } + + ReadStream(const void* data, size_t size) + : Stream(const_cast(data), size) + { } + + ReadStream(const ReadStream& stream) + : Stream(stream) + { } + + ReadStream(const ReadStream& stream, size_t size) + : Stream(stream, size) + { } + + // throw (UnderrunError) + void checkReadable(size_t n) const + { + if (consumable() < n) throw UnderrunError(); + } + + // throw (UnderrunError) + void skip(size_t n) + { + checkReadable(n); + advance(n); + } + + // throw (UnderrunError) + inline char peekChar() const + { + checkReadable(1); + return *pos(); + } + + // throw (UnderrunError) + inline char getChar() + { + const char x = peekChar(); + advance(1); + return x; + } + + // throw (UnderrunError) + inline int32_t peekInt32() const + { + checkReadable(4); + checkAlignment(4); + uint32_t un; + std::memcpy(&un, pos(), 4); + const uint32_t uh = convert32(un); + int32_t x; + std::memcpy(&x, &uh, 4); + return x; + } + + // throw (UnderrunError) + inline int32_t getInt32() + { + const int32_t x = peekInt32(); + advance(4); + return x; + } + + // throw (UnderrunError) + inline uint64_t getUInt64() + { + checkReadable(8); + uint64_t un; + std::memcpy(&un, pos(), 8); + advance(8); + return convert64(un); + } + + // throw (UnderrunError) + inline float getFloat32() + { + checkReadable(4); + checkAlignment(4); + uint32_t un; + std::memcpy(&un, pos(), 4); + advance(4); + const uint32_t uh = convert32(un); + float f; + std::memcpy(&f, &uh, 4); + return f; + } + + // throw (UnderrunError, ParseError) + const char* getString() + { + checkReadable(4); // min string length + + const char* ptr = static_cast(pos()) + 3; + const char* end = static_cast(this->end()); + + while (true) { + if (ptr >= end) throw UnderrunError(); + if (*ptr == '\0') break; + ptr += 4; + } + + const char* x = pos(); + advance(ptr - pos() + 1); + + return x; + } +}; +} + +#endif // OSCPP_STREAM_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/error.hpp b/java/libraries/sound/src/cpp/include/oscpp/error.hpp new file mode 100644 index 000000000..bea43e74b --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/error.hpp @@ -0,0 +1,84 @@ +// oscpp library +// +// Copyright (c) 2004-2013 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_ERROR_HPP_INCLUDED +#define OSCPP_ERROR_HPP_INCLUDED + +#include +#include + +namespace OSCPP { + +class Error : public std::exception +{ +public: + Error(const std::string& what) + : m_what(what) + { } + + virtual ~Error() noexcept + { } + + const char* what() const noexcept override + { + return m_what.c_str(); + } + +private: + std::string m_what; +}; + +class UnderrunError : public Error +{ +public: + UnderrunError() + : Error(std::string("Buffer underrun")) + { } +}; + +class OverflowError : public Error +{ +public: + OverflowError(size_t bytes) + : Error(std::string("Buffer overflow")), + m_bytes(bytes) + { } + + size_t numBytes() const { return m_bytes; } + +private: + size_t m_bytes; +}; + +class ParseError : public Error +{ +public: + ParseError(const std::string& what="Parse error") + : Error(what) + { } +}; + +} + +#endif // OSCPP_ERROR_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/print.hpp b/java/libraries/sound/src/cpp/include/oscpp/print.hpp new file mode 100644 index 000000000..448e20b61 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/print.hpp @@ -0,0 +1,166 @@ +// OSCpp library +// +// Copyright (c) 2004-2011 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_PRINT_HPP_INCLUDED +#define OSCPP_PRINT_HPP_INCLUDED + +#include +#include +#include + +namespace OSCPP { +namespace detail { + +const size_t kDefaultIndentWidth = 4; + +class Indent +{ +public: + Indent(size_t w) + : m_width(w) + , m_indent(0) + { } + Indent(size_t w, size_t n) + : m_width(w) + , m_indent(n) + { } + Indent(const Indent&) = default; + + operator size_t () const { return m_indent; } + Indent inc() const { return Indent(m_width, m_indent+m_width); } + +private: + size_t m_width; + size_t m_indent; +}; + +inline std::ostream& operator<<(std::ostream& out, const Indent& indent) +{ + size_t n = indent; + while (n-- > 0) out << ' '; + return out; +} + +inline void printArgs(std::ostream& out, Server::ArgStream args) +{ + while (!args.atEnd()) { + const char t = args.tag(); + switch (t) { + case 'i': + out << "i:" << args.int32(); + break; + case 'f': + out << "f:" << args.float32(); + break; + case 's': + out << "s:" << args.string(); + break; + case 'b': + out << "b:" << args.blob().size(); + break; + case '[': + out << "[ "; + printArgs(out, args.array()); + out << " ]"; + break; + default: + out << t << ":?"; + args.drop(); + break; + } + out << ' '; + } +} + +inline void printMessage(std::ostream& out, const Server::Message& msg, const Indent& indent) +{ + out << indent << msg.address() << ' '; + printArgs(out, msg.args()); +} + +inline void printBundle(std::ostream& out, const Server::Bundle& bundle, const Indent& indent) +{ + out << indent << "# " << bundle.time() << " [" << std::endl; + Indent nextIndent = indent.inc(); + auto packets = bundle.packets(); + while (!packets.atEnd()) { + auto packet = packets.next(); + if (packet.isMessage()) { + printMessage(out, packet, nextIndent); + } else { + printBundle(out, packet, nextIndent); + } + out << std::endl; + } + out << indent << "]"; +} + +inline void printPacket(std::ostream& out, const Server::Packet& packet, const Indent& indent) +{ + if (packet.isMessage()) { + printMessage(out, packet, indent); + } else { + printBundle(out, packet, indent); + } +} + +} +} + +namespace OSCPP { +namespace Server { + +inline std::ostream& operator<<(std::ostream& out, const Packet& packet) +{ + detail::printPacket(out, packet, detail::Indent(detail::kDefaultIndentWidth)); + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const Bundle& packet) +{ + detail::printBundle(out, packet, detail::Indent(detail::kDefaultIndentWidth)); + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const Message& packet) +{ + detail::printMessage(out, packet, detail::Indent(detail::kDefaultIndentWidth)); + return out; +} + +} +} + +namespace OSCPP { +namespace Client { + +inline std::ostream& operator<<(std::ostream& out, const Packet& packet) +{ + return out << Server::Packet(packet.data(), packet.size()); +} + +} +} + +#endif // OSCPP_PRINT_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/server.hpp b/java/libraries/sound/src/cpp/include/oscpp/server.hpp new file mode 100644 index 000000000..a08f0ea0f --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/server.hpp @@ -0,0 +1,450 @@ +// oscpp library +// +// Copyright (c) 2004-2013 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_SERVER_HPP_INCLUDED +#define OSCPP_SERVER_HPP_INCLUDED + +#include +#include + +#include +#include +#include + +namespace OSCPP { +namespace Server { + +//! OSC Message Argument Iterator. +/*! + * Retrieve typed arguments from an incoming message. + * + * Supported tags and their correspondong types are: + * + * i -- 32 bit signed integer number
+ * f -- 32 bit floating point number
+ * s -- NULL-terminated string padded to 4-byte boundary
+ * b -- 32-bit integer size followed by 4-byte aligned data + * + * \sa getArgInt32 + * \sa getArgFloat32 + * \sa getArgString + */ +class ArgStream +{ +public: + //* Empty argument stream. + ArgStream() = default; + + //* Construct argument stream from tag and value streams. + ArgStream(const ReadStream& tags, const ReadStream& args) + : m_tags(tags) + , m_args(args) + { } + + //! Constructor. + /*! + * Read arguments from stream, which has to point to the start of a + * message type signature. + * + * \throw OSCPP::UnderrunError stream buffer underrun. + * \throw OSCPP::ParseError error while parsing input stream. + */ + ArgStream(const ReadStream& stream) + { + m_args = stream; + const char* tags = m_args.getString(); + if (tags[0] != ',') throw ParseError("Tag string doesn't start with ','"); + m_tags = ReadStream(tags+1, strlen(tags)-1); + } + + //* Return the number of arguments that can be read from the stream. + size_t size() const + { + return m_tags.capacity(); + } + + //* Return true if no more arguments can be read from the stream. + bool atEnd() const + { + return m_tags.atEnd(); + } + + //* Return tag and argument streams. + std::tuple state() const + { + return std::make_tuple(m_tags, m_args); + } + + //* Return the type tag corresponding to the next message argument. + char tag() const + { + return m_tags.peekChar(); + } + + //* Drop next argument. + void drop() + { + drop(m_tags.getChar()); + } + + //! Get next integer argument. + /*! + * Read next numerical argument from the input stream and convert it to + * an integer. + * + * \exception OSCPP::UnderrunError stream buffer underrun. + * \exception OSCPP::ParseError argument could not be converted. + */ + int32_t int32() + { + const char t = m_tags.getChar(); + if (t == 'i') return m_args.getInt32(); + if (t == 'f') return (int32_t)m_args.getFloat32(); + throw ParseError("Cannot convert argument to int"); + } + + //! Get next float argument. + /*! + * Read next numerical argument from the input stream and convert it to + * a float. + * + * \exception OSCPP::UnderrunError stream buffer underrun. + * \exception OSCPP::ParseError argument could not be converted. + */ + float float32() + { + const char t = m_tags.getChar(); + if (t == 'f') return m_args.getFloat32(); + if (t == 'i') return (float)m_args.getInt32(); + throw ParseError("Cannot convert argument to float"); + } + + //! Get next string argument. + /*! + * Read next string argument and return it as a NULL-terminated string. + * + * \exception OSCPP::UnderrunError stream buffer underrun. + * \exception OSCPP::ParseError argument could not be converted or is not + * a valid string. + */ + const char* string() + { + if (m_tags.getChar() == 's') { + return m_args.getString(); + } + throw ParseError("Cannot convert argument to string"); + } + + //* Get next blob argument. + // + // @throw OSCPP::UnderrunError stream buffer underrun. + // @throw OSCPP::ParseError argument is not a valid blob + Blob blob() + { + if (m_tags.getChar() == 'b') { + return parseBlob(); + } else { + throw ParseError("Cannot convert argument to blob"); + } + } + + //* Return a stream corresponding to an array argument. + ArgStream array() + { + if (m_tags.getChar() == '[') { + const char* tags = m_tags.pos(); + const char* args = m_args.pos(); + dropArray(); + // m_tags.pos() points right after the closing ']'. + return ArgStream(ReadStream(tags, m_tags.pos() - tags - 1), + ReadStream(args, m_args.pos() - args)); + } else { + throw ParseError("Expected array"); + } + } + + template T next() + { + return T::OSC_Server_ArgStream_next_unimplemented; + } + +private: + // Parse a blob (type tag already consumed). + Blob parseBlob() + { + int32_t size = m_args.getInt32(); + if (size < 0) { + throw ParseError("Invalid blob size is less than zero"); + } else { + static_assert(sizeof(size_t) >= sizeof(int32_t), + "Size of size_t must be greater than size of int32_t"); + const void* data = m_args.pos(); + m_args.skip(align(size)); + return Blob(data, static_cast(size)); + } + } + // Drop an atomic value of type t (type tag already consumed). + void dropAtom(char t) + { + switch (t) { + case 'i': m_args.skip(4); break; + case 'f': m_args.skip(4); break; + case 's': m_args.getString(); break; + case 'b': parseBlob(); break; + } + } + // Drop a possibly nested array. + void dropArray() + { + unsigned int level = 0; + for (;;) { + char t = m_tags.getChar(); + if (t == ']') { + if (level == 0) break; + else level--; + } else if (t == '[') { + level++; + } else { + dropAtom(t); + } + } + } + // Drop the next argument of type t (type tag already consumed). + void drop(char t) + { + switch (t) { + case '[': dropArray(); break; + default: dropAtom(t); + } + } + +private: + ReadStream m_tags; + ReadStream m_args; +}; + +class Message +{ +public: + Message(const char* address, const ReadStream& stream) + : m_address(address) + , m_args(ArgStream(stream)) + { } + + const char* address() const + { + return m_address; + } + + ArgStream args() const + { + return m_args; + } + +private: + const char* m_address; + ArgStream m_args; +}; + +class PacketStream; + +class Bundle +{ +public: + Bundle(uint64_t time, const ReadStream& stream) + : m_time(time) + , m_stream(stream) + { } + + uint64_t time() const + { + return m_time; + } + + inline PacketStream packets() const; + +private: + uint64_t m_time; + ReadStream m_stream; +}; + +class Packet +{ +public: + Packet() + : m_isBundle(false) + { } + + Packet(const ReadStream& stream) + : m_stream(stream) + , m_isBundle(isBundle(stream)) + { + // Skip over #bundle header + if (m_isBundle) m_stream.skip(8); + } + + Packet(const void* data, size_t size) + : Packet(ReadStream(data, size)) + { } + + const void* data() const + { + return m_stream.begin(); + } + + size_t size() const + { + return m_stream.capacity(); + } + + bool isBundle() const + { + return m_isBundle; + } + + bool isMessage() const + { + return !isBundle(); + } + + operator Bundle () const + { + if (!isBundle()) throw ParseError("Packet is not a bundle"); + ReadStream stream(m_stream); + uint64_t time = stream.getUInt64(); + return Bundle(time, std::move(stream)); + } + + operator Message () const + { + if (!isMessage()) throw ParseError("Packet is not a message"); + ReadStream stream(m_stream); + const char* address = stream.getString(); + return Message(address, std::move(stream)); + } + + static bool isMessage(const void* data, size_t size) + { + return (size > 3) && (static_cast(data)[0] != '#'); + } + + static bool isMessage(const ReadStream& stream) + { + return isMessage(stream.pos(), stream.consumable()); + } + + static bool isBundle(const void* data, size_t size) + { + return (size > 15) && (std::memcmp(data, "#bundle", 8) == 0); + } + + static bool isBundle(const ReadStream& stream) + { + return isBundle(stream.pos(), stream.consumable()); + } + +private: + ReadStream m_stream; + bool m_isBundle; +}; + + +class PacketStream +{ +public: + PacketStream(const ReadStream& stream) + : m_stream(stream) + { } + + bool atEnd() const + { + return m_stream.atEnd(); + } + + Packet next() + { + size_t size = m_stream.getInt32(); + ReadStream stream(m_stream, size); + m_stream.skip(size); + return Packet(stream); + } + +private: + ReadStream m_stream; +}; + +template <> inline int32_t ArgStream::next() +{ + return int32(); +} + +template <> inline float ArgStream::next() +{ + return float32(); +} + +template <> inline const char* ArgStream::next() +{ + return string(); +} + +template <> inline Blob ArgStream::next() +{ + return blob(); +} + +template <> inline ArgStream ArgStream::next() +{ + return array(); +} + +PacketStream Bundle::packets() const +{ + return PacketStream(m_stream); +} + +} +} + +static inline bool operator==(const OSCPP::Server::Message& msg, const char* str) +{ + return strcmp(msg.address(), str) == 0; +} + +static inline bool operator==(const char* str, const OSCPP::Server::Message& msg) +{ + return msg == str; +} + +static inline bool operator!=(const OSCPP::Server::Message& msg, const char* str) +{ + return !(msg == str); +} + +static inline bool operator!=(const char* str, const OSCPP::Server::Message& msg) +{ + return msg != str; +} + +#endif // OSCPP_SERVER_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/types.hpp b/java/libraries/sound/src/cpp/include/oscpp/types.hpp new file mode 100644 index 000000000..636a7cddd --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/types.hpp @@ -0,0 +1,51 @@ +// oscpp library +// +// Copyright (c) 2004-2013 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_TYPES_HPP_INCLUDED +#define OSCPP_TYPES_HPP_INCLUDED + +namespace OSCPP { + +class Blob +{ +public: + Blob() + : m_size(0), m_data(nullptr) + { } + Blob(const void* data, size_t size) + : m_size(size), m_data(data) + { } + Blob(const Blob& other) = default; + + size_t size() const { return m_size; } + const void* data() const { return m_data; } + +private: + size_t m_size; + const void* m_data; +}; + +} + +#endif // OSCPP_TYPES_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/oscpp/util.hpp b/java/libraries/sound/src/cpp/include/oscpp/util.hpp new file mode 100644 index 000000000..40fd52e7a --- /dev/null +++ b/java/libraries/sound/src/cpp/include/oscpp/util.hpp @@ -0,0 +1,151 @@ +// oscpp library +// +// Copyright (c) 2004-2013 Stefan Kersten +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef OSCPP_UTIL_HPP_INCLUDED +#define OSCPP_UTIL_HPP_INCLUDED + +#include +#include + +namespace OSCPP { + +static const size_t kAlignment = 4; + +inline bool isAligned(const void* ptr, size_t alignment) +{ + return (reinterpret_cast(ptr) & (alignment-1)) == 0; +} + +constexpr bool isAligned(size_t n) +{ + return (n & 3) == 0; +} + +constexpr size_t align(size_t n) +{ + return (n + 3) & -4; +} + +constexpr size_t padding(size_t n) +{ + return align(n) - n; +} + +inline void checkAlignment(const void* ptr, size_t n) +{ + if (!isAligned(ptr, n)) { + throw std::runtime_error("Unaligned pointer"); + } +} + +namespace Tags { + + constexpr size_t int32() + { + return 1; + } + constexpr size_t float32() + { + return 1; + } + constexpr size_t string() + { + return 1; + } + constexpr size_t blob() + { + return 1; + } + constexpr size_t array(size_t numElems) + { + return numElems+2; + } +} + +namespace Size { + + class String + { + public: + String(const char* x) + : m_value(x) + {} + + operator const char* () const + { + return m_value; + } + + private: + const char* m_value; + }; + + inline size_t string(const String& x) + { + return align(std::strlen(x)+1); + } + + template constexpr size_t string(char const (&)[N]) + { + return align(N); + } + + constexpr size_t bundle(size_t numPackets) + { + return 8 /* #bundle */ + 8 /* timestamp */ + 4 * numPackets /* size prefix */; + } + + inline size_t message(const String& address, size_t numArgs) + { + return string(address) + align(numArgs + 2); + } + + template constexpr size_t message(char const (&address)[N], size_t numArgs) + { + return string(address) + align(numArgs + 2); + } + + constexpr size_t int32(size_t n=1) + { + return n*4; + } + + constexpr size_t float32(size_t n=1) + { + return n*4; + } + + constexpr size_t string(size_t n) + { + return align(n + 1); + } + + constexpr size_t blob(size_t size) + { + return 4 + align(size); + } +} +} + +#endif // OSCPP_UTIL_HPP_INCLUDED diff --git a/java/libraries/sound/src/cpp/include/processing_sound_MethClaInterface.h b/java/libraries/sound/src/cpp/include/processing_sound_MethClaInterface.h new file mode 100644 index 000000000..1e8107a64 --- /dev/null +++ b/java/libraries/sound/src/cpp/include/processing_sound_MethClaInterface.h @@ -0,0 +1,277 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class processing_sound_MethClaInterface */ + +#ifndef _Included_processing_sound_MethClaInterface +#define _Included_processing_sound_MethClaInterface +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: processing_sound_MethClaInterface + * Method: engineNew + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_engineNew + (JNIEnv *, jobject, jint, jint); + +/* + * Class: processing_sound_MethClaInterface + * Method: engineStart + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_engineStart + (JNIEnv *, jobject); + +/* + * Class: processing_sound_MethClaInterface + * Method: engineStop + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_engineStop + (JNIEnv *, jobject); + +/* + * Class: processing_sound_MethClaInterface + * Method: synthStop + * Signature: ([I)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_synthStop + (JNIEnv *, jobject, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: oscSet + * Signature: (FFFF[I)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_oscSet + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: sinePlay + * Signature: (FFFF)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_sinePlay + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: sawPlay + * Signature: (FFFF)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_sawPlay + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: sqrPlay + * Signature: (FFFF)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_sqrPlay + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: sqrSet + * Signature: (FFFF[I)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_sqrSet + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: triPlay + * Signature: (FFFF)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_triPlay + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: pulsePlay + * Signature: (FFFFF)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_pulsePlay + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: pulseSet + * Signature: (FFFFF[I)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_pulseSet + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat, jfloat, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: soundFileInfo + * Signature: (Ljava/lang/String;)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_soundFileInfo + (JNIEnv *, jobject, jstring); + +/* + * Class: processing_sound_MethClaInterface + * Method: soundFilePlayMono + * Signature: (FFFFZLjava/lang/String;FI)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_soundFilePlayMono + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat, jboolean, jstring, jfloat, jint); + +/* + * Class: processing_sound_MethClaInterface + * Method: soundFilePlayMulti + * Signature: (FFFZLjava/lang/String;FI)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_soundFilePlayMulti + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jboolean, jstring, jfloat, jint); + +/* + * Class: processing_sound_MethClaInterface + * Method: soundFileSetMono + * Signature: (FFFF[I)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_soundFileSetMono + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jfloat, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: soundFileSetStereo + * Signature: (FFF[I)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_soundFileSetStereo + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: whiteNoisePlay + * Signature: (FFF)[I + */ +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_whiteNoisePlay + (JNIEnv *, jobject, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: whiteNoiseSet + * Signature: (FFF[I)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_whiteNoiseSet + (JNIEnv *, jobject, jfloat, jfloat, jfloat, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: envelopePlay + * Signature: ([IFFFF)I + */ +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_envelopePlay + (JNIEnv *, jobject, jintArray, jfloat, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: doneAfter + * Signature: (F)I + */ +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_doneAfter + (JNIEnv *, jobject, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: highPassPlay + * Signature: ([IFF)I + */ +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_highPassPlay + (JNIEnv *, jobject, jintArray, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: lowPassPlay + * Signature: ([IFF)I + */ +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_lowPassPlay + (JNIEnv *, jobject, jintArray, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: bandPassPlay + * Signature: ([IFF)I + */ +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_bandPassPlay + (JNIEnv *, jobject, jintArray, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: filterSet + * Signature: (FFI)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_filterSet + (JNIEnv *, jobject, jfloat, jfloat, jint); + +/* + * Class: processing_sound_MethClaInterface + * Method: delayPlay + * Signature: ([IFFF)I + */ +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_delayPlay + (JNIEnv *, jobject, jintArray, jfloat, jfloat, jfloat); + +/* + * Class: processing_sound_MethClaInterface + * Method: delaySet + * Signature: (FFI)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_delaySet + (JNIEnv *, jobject, jfloat, jfloat, jint); + +/* + * Class: processing_sound_MethClaInterface + * Method: amplitude + * Signature: ([I)J + */ +JNIEXPORT jlong JNICALL Java_processing_sound_MethClaInterface_amplitude + (JNIEnv *, jobject, jintArray); + +/* + * Class: processing_sound_MethClaInterface + * Method: poll_amplitude + * Signature: (J)F + */ +JNIEXPORT jfloat JNICALL Java_processing_sound_MethClaInterface_poll_1amplitude + (JNIEnv *, jobject, jlong); + +/* + * Class: processing_sound_MethClaInterface + * Method: destroy_amplitude + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_destroy_1amplitude + (JNIEnv *, jobject, jlong); + +/* + * Class: processing_sound_MethClaInterface + * Method: fft + * Signature: ([II)J + */ +JNIEXPORT jlong JNICALL Java_processing_sound_MethClaInterface_fft + (JNIEnv *, jobject, jintArray, jint); + +/* + * Class: processing_sound_MethClaInterface + * Method: poll_fft + * Signature: (J)[F + */ +JNIEXPORT jfloatArray JNICALL Java_processing_sound_MethClaInterface_poll_1fft + (JNIEnv *, jobject, jlong); + +/* + * Class: processing_sound_MethClaInterface + * Method: destroy_fft + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_destroy_1fft + (JNIEnv *, jobject, jlong); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/java/libraries/sound/src/cpp/processing_sound_MethClaInterface.cpp b/java/libraries/sound/src/cpp/processing_sound_MethClaInterface.cpp new file mode 100644 index 000000000..65757acd4 --- /dev/null +++ b/java/libraries/sound/src/cpp/processing_sound_MethClaInterface.cpp @@ -0,0 +1,962 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "processing_sound_MethClaInterface.h" +#include "methcla/file.hpp" +#include "methcla/engine.hpp" +#include "methcla/plugins/sine.h" +#include "methcla/plugins/saw.h" +#include "methcla/plugins/tri.h" +#include "methcla/plugins/pulse.h" +#include "methcla/plugins/patch-cable.h" +#include "methcla/plugins/soundfile_api_libsndfile.h" +#include "methcla/plugins/sampler.h" +#include "methcla/plugins/whitenoise.h" +#include "methcla/plugins/node-control.h" +#include "methcla/plugins/pan2.h" +#include "methcla/plugins/soundfile_api_mpg123.h" +#include "methcla/plugins/ampfol.h" +#include "methcla/plugins/fft.h" +#include "methcla/plugins/hpf.h" +#include "methcla/plugins/lpf.h" +#include "methcla/plugins/delay.h" + +#define OUTPUT_BUFFER_SIZE 1024 +#define SNDF_BUFFER_LEN 1024 + +#define MAX_CHANNELS 2 + +Methcla::Engine* m_engine; +Methcla::Engine& engine() { return *m_engine; } +std::mutex mutex; + +static Methcla_Time kLatency = 0.1; + +typedef struct data_v{ + int id; + std::atomic amp; +} ServerValue, *ServerValuePtr; + +typedef struct data_a{ + int id; + int fftSize=512; + std::vector fft; +} ServerArray, *ServerArrayPtr; + + +// Engine + +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_engineNew (JNIEnv *, jobject, jint sampleRate, jint bufferSize){ + + Methcla::EngineOptions options; + options.audioDriver.bufferSize = bufferSize; + options.realtimeMemorySize = 1024 * 1024 * 20; + options.maxNumNodes = 1024 * 20; + options.addLibrary(methcla_plugins_sine) + .addLibrary(methcla_plugins_saw) + .addLibrary(methcla_plugins_tri) + .addLibrary(methcla_plugins_pulse) + .addLibrary(methcla_soundfile_api_libsndfile) + .addLibrary(methcla_plugins_patch_cable) + .addLibrary(methcla_plugins_sampler) + .addLibrary(methcla_plugins_white_noise) + .addLibrary(methcla_plugins_node_control) + .addLibrary(methcla_plugins_pan2) + .addLibrary(methcla_plugins_amplitude_follower) + .addLibrary(methcla_plugins_hpf) + .addLibrary(methcla_plugins_lpf) + .addLibrary(methcla_plugins_delay) + .addLibrary(methcla_plugins_fft); + + m_engine = new Methcla::Engine(options); + + return 1; +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_engineStart(JNIEnv *env, jobject object){ + engine().start(); +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_engineStop(JNIEnv *env, jobject object){ + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.freeAll(engine().root()); + request.closeBundle(); + request.send(); + + engine().stop(); + //delete m_engine; + +}; + +// General Synth + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_synthStop(JNIEnv *env, jobject object, jintArray nodeId){ + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.free(m_nodeId[0]); + request.free(m_nodeId[1]); + + request.closeBundle(); + engine().addNotificationHandler(engine().freeNodeIdHandler(m_nodeId[0])); + engine().addNotificationHandler(engine().freeNodeIdHandler(m_nodeId[1])); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + +}; + +// General Oscillator set method + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_oscSet (JNIEnv *env, jobject object, jfloat freq, jfloat amp, jfloat add, jfloat pos, jintArray nodeId){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(m_nodeId[0], 0 , freq); + request.set(m_nodeId[0], 1 , amp); + request.set(m_nodeId[0], 2 , add); + request.set(m_nodeId[1], 0, pos); + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); +}; + +// SineOsc + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_sinePlay(JNIEnv *env, jobject object, jfloat freq, jfloat amp, jfloat add, jfloat pos){ + + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::AudioBusId bus = m_engine->audioBusId().alloc(); + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_SINE_URI, + engine().root(), + {freq, amp, add} + ); + + auto pan = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + {pos, 1.f}, + {Methcla::Value(1.f)} + ); + + request.mapOutput(synth.id(), 0, bus); + request.mapInput(pan.id(), 0, bus); + request.mapOutput(pan.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(pan.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.activate(synth.id()); + request.activate(pan.id()); + + request.closeBundle(); + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=pan.id(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +}; + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_sawPlay(JNIEnv *env, jobject object, jfloat freq, jfloat amp, jfloat add, jfloat pos){ + + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::AudioBusId bus = m_engine->audioBusId().alloc(); + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_SAW_URI, + engine().root(), + {freq, amp, add} + ); + + auto pan = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + {pos, 1.f}, + {Methcla::Value(1.f)} + ); + + request.mapOutput(synth.id(), 0, bus); + request.mapInput(pan.id(), 0, bus); + request.mapOutput(pan.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(pan.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.activate(synth.id()); + request.activate(pan.id()); + + request.closeBundle(); + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=pan.id(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +}; + + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_triPlay(JNIEnv *env, jobject object, jfloat freq, jfloat amp, jfloat add, jfloat pos){ + + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::AudioBusId bus = m_engine->audioBusId().alloc(); + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_TRI_URI, + engine().root(), + {freq, amp, add} + ); + + auto pan = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + {pos, 1.f}, + {Methcla::Value(1.f)} + ); + + request.mapOutput(synth.id(), 0, bus); + request.mapInput(pan.id(), 0, bus); + request.mapOutput(pan.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(pan.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.activate(synth.id()); + request.activate(pan.id()); + + request.closeBundle(); + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=pan.id(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +} + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_sqrPlay(JNIEnv *env, jobject object, jfloat freq, jfloat amp, jfloat add, jfloat pos){ + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::AudioBusId bus = m_engine->audioBusId().alloc(); + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_PULSE_URI, + engine().root(), + {freq, 0.5, amp*2.f, add-1.f} + ); + + auto pan = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + {pos, 1.f}, + {Methcla::Value(1.f)} + ); + + request.mapOutput(synth.id(), 0, bus); + request.mapInput(pan.id(), 0, bus); + request.mapOutput(pan.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(pan.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.activate(synth.id()); + request.activate(pan.id()); + + request.closeBundle(); + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=pan.id(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_sqrSet(JNIEnv *env, jobject object, jfloat freq, jfloat amp, jfloat add, jfloat pos, jintArray nodeId){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(m_nodeId[0], 0 , freq); + request.set(m_nodeId[0], 2 , amp); + request.set(m_nodeId[1], 0 , pos); + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); +}; + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_pulsePlay(JNIEnv *env, jobject object, jfloat freq, jfloat width, jfloat amp, jfloat add, jfloat pos){ + + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::AudioBusId bus = m_engine->audioBusId().alloc(); + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_PULSE_URI, + engine().root(), + {freq, width, amp, add} + ); + + auto pan = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + {pos, 1.f}, + {Methcla::Value(1.f)} + ); + + request.mapOutput(synth.id(), 0, bus); + request.mapInput(pan.id(), 0, bus); + request.mapOutput(pan.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(pan.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.activate(synth.id()); + request.activate(pan.id()); + + request.closeBundle(); + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=pan.id(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +} + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_pulseSet(JNIEnv *env, jobject object, jfloat freq, jfloat width, jfloat amp, jfloat add, jfloat pos, jintArray nodeId){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(m_nodeId[0], 0 , freq); + request.set(m_nodeId[0], 1 , width); + request.set(m_nodeId[0], 2 , amp); + request.set(m_nodeId[1], 0 , pos); + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); +}; + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_soundFileInfo(JNIEnv *env, jobject object, jstring path){ + const char *str = env->GetStringUTFChars(path, 0); + + Methcla::SoundFile file(engine(), str); + + jintArray info = env->NewIntArray(3); + jint *temp = env->GetIntArrayElements(info, NULL); + + temp[0] = file.info().frames; + temp[1] = file.info().samplerate; + temp[2] = file.info().channels; + + env->ReleaseIntArrayElements(info, temp, 0); + env->ReleaseStringUTFChars(path, str); + + return info; +}; + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_soundFilePlayMono (JNIEnv *env, jobject object, jfloat rate, jfloat pos, jfloat amp, jfloat add, jboolean loop, jstring path, jfloat dur, jint cue){ + + const char *str = env->GetStringUTFChars(path, 0); + + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::Request request(engine()); + Methcla::NodeTreeStatistics results; + + Methcla::AudioBusId bus = m_engine->audioBusId().alloc(); + + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_SAMPLER_URI, + engine().root(), + { amp, rate }, + { Methcla::Value(str), + Methcla::Value(loop), + Methcla::Value(cue) } + ); + + auto pan = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + {pos, 1.f}, + {Methcla::Value(1.f)} + ); + + auto after = request.synth( + METHCLA_PLUGINS_DONE_AFTER_URI, + engine().root(), + { }, + { Methcla::Value(dur) } + ); + + request.mapOutput(synth.id(), 0, bus); + request.mapInput(pan.id(), 0, bus); + request.mapOutput(pan.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(pan.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.whenDone(after.id(), Methcla::kNodeDoneFreeSelf | Methcla::kNodeDoneFreePreceeding); + request.activate(synth.id()); + request.activate(pan.id()); + if (loop == false) + { + request.activate(after.id()); + } + request.closeBundle(); + + engine().addNotificationHandler(engine().freeNodeIdHandler(synth.id())); + engine().addNotificationHandler(engine().freeNodeIdHandler(pan.id())); + engine().addNotificationHandler(engine().freeNodeIdHandler(after.id())); + + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=pan.id(); + + //results = engine().getNodeTreeStatistics(); + //std::cout << results.numSynths << std::endl; + + env->ReleaseStringUTFChars(path, str); + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +}; + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_soundFilePlayMulti(JNIEnv *env, jobject object, jfloat rate, jfloat amp, jfloat add, jboolean loop, jstring path, jfloat dur, jint cue){ + const char *str = env->GetStringUTFChars(path, 0); + + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::Request request(engine()); + Methcla::NodeTreeStatistics results; + + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_SAMPLER_URI, + engine().root(), + { amp, rate }, + { Methcla::Value(str), + Methcla::Value(loop), + Methcla::Value(cue) } + ); + + auto after = request.synth( + METHCLA_PLUGINS_DONE_AFTER_URI, + engine().root(), + { }, + { Methcla::Value(dur) } + ); + + request.mapOutput(synth.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(synth.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.whenDone(after.id(), Methcla::kNodeDoneFreeSelf | Methcla::kNodeDoneFreePreceeding); + request.activate(synth.id()); + request.activate(after.id()); + request.closeBundle(); + + engine().addNotificationHandler(engine().freeNodeIdHandler(synth.id())); + engine().addNotificationHandler(engine().freeNodeIdHandler(after.id())); + + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=after.id(); + + //results = engine().getNodeTreeStatistics(); + //std::cout << results.numSynths << std::endl; + + env->ReleaseStringUTFChars(path, str); + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +} + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_soundFileSetMono (JNIEnv *env, jobject object, jfloat rate, jfloat pos, jfloat amp, jfloat add, jintArray nodeId){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(m_nodeId[0], 0, amp); + request.set(m_nodeId[0], 1, rate); + request.set(m_nodeId[1], 0, pos); + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_soundFileSetStereo(JNIEnv *env, jobject object, jfloat rate, jfloat amp, jfloat add, jintArray nodeId){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(m_nodeId[0], 0, amp); + request.set(m_nodeId[0], 1, rate); + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + +}; + +JNIEXPORT jintArray JNICALL Java_processing_sound_MethClaInterface_whiteNoisePlay(JNIEnv *env, jobject object, jfloat amp, jfloat add, jfloat pos){ + + jintArray nodeId = env->NewIntArray(2); + jint *m_nodeId = env->GetIntArrayElements(nodeId, NULL); + + Methcla::AudioBusId bus = m_engine->audioBusId().alloc(); + Methcla::Request request(engine()); + + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_WHITE_NOISE_URI, + engine().root(), + { amp, add }, + {Methcla::Value(0.0)} + ); + + auto pan = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + {pos, 1.f}, + {Methcla::Value(1.f)} + ); + + request.mapOutput(synth.id(), 0, bus); + request.mapInput(pan.id(), 0, bus); + request.mapOutput(pan.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.mapOutput(pan.id(), 1, Methcla::AudioBusId(1), Methcla::kBusMappingExternal); + + request.activate(synth.id()); + request.activate(pan.id()); + + request.closeBundle(); + request.send(); + + m_nodeId[0]=synth.id(); + m_nodeId[1]=pan.id(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return nodeId; +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_whiteNoiseSet(JNIEnv *env, jobject object, jfloat amp, jfloat add, jfloat pos, jintArray nodeId){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(m_nodeId[0], 0, amp); + request.set(m_nodeId[1], 0, pos); + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); +}; + +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_envelopePlay(JNIEnv *env, jobject object, jintArray nodeId, jfloat attackTime, jfloat sustainTime, jfloat sustainLevel, jfloat releaseTime){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::AudioBusId in_bus = m_engine->audioBusId().alloc(); + Methcla::AudioBusId out_bus = m_engine->audioBusId().alloc(); + + + const std::list envOptions = + { Methcla::Value(attackTime) + , Methcla::Value(sustainTime) + , Methcla::Value(sustainLevel) + , Methcla::Value(releaseTime) + }; + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_ASR_ENVELOPE_URI, + Methcla::NodePlacement::after(m_nodeId[0]), + {}, + envOptions + ); + + request.mapOutput(m_nodeId[0], 0, in_bus); + request.mapInput(synth.id(), 0, in_bus); + request.mapOutput(synth.id(), 0, out_bus); + request.mapInput(m_nodeId[1], 0, out_bus); + + request.activate(synth.id()); + + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return synth.id(); +}; + +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_highPassPlay(JNIEnv *env, jobject object, jintArray nodeId, jfloat freq, jfloat res){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::AudioBusId in_bus = m_engine->audioBusId().alloc(); + Methcla::AudioBusId out_bus = m_engine->audioBusId().alloc(); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_HPF_URI, + Methcla::NodePlacement::after(m_nodeId[0]), + {freq, res}, + {} + ); + + request.mapOutput(m_nodeId[0], 0, in_bus); + request.mapInput(synth.id(), 0, in_bus); + request.mapOutput(synth.id(), 0, out_bus); + request.mapInput(m_nodeId[1], 0, out_bus); + + request.activate(synth.id()); + + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return synth.id(); +}; + +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_lowPassPlay(JNIEnv *env, jobject object, jintArray nodeId, jfloat freq, jfloat res){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::AudioBusId in_bus = m_engine->audioBusId().alloc(); + Methcla::AudioBusId out_bus = m_engine->audioBusId().alloc(); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_LPF_URI, + Methcla::NodePlacement::after(m_nodeId[0]), + {freq, res}, + {} + ); + + request.mapOutput(m_nodeId[0], 0, in_bus); + request.mapInput(synth.id(), 0, in_bus); + request.mapOutput(synth.id(), 0, out_bus); + request.mapInput(m_nodeId[1], 0, out_bus); + + request.activate(synth.id()); + + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return synth.id(); +}; + +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_bandPassPlay(JNIEnv *env, jobject object, jintArray nodeId, jfloat freq, jfloat res){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::AudioBusId in_bus = m_engine->audioBusId().alloc(); + Methcla::AudioBusId out_bus = m_engine->audioBusId().alloc(); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_HPF_URI, + Methcla::NodePlacement::after(m_nodeId[0]), + {freq, res}, + {} + ); + + request.mapOutput(m_nodeId[0], 0, in_bus); + request.mapInput(synth.id(), 0, in_bus); + request.mapOutput(synth.id(), 0, out_bus); + request.mapInput(m_nodeId[1], 0, out_bus); + + request.activate(synth.id()); + + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return synth.id(); +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_filterSet(JNIEnv *env, jobject object, jfloat freq, jfloat res, jint nodeId){ + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(nodeId, 0, freq); + request.set(nodeId, 1, res); + request.closeBundle(); + request.send(); +}; + +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_delayPlay(JNIEnv *env, jobject object, jintArray nodeId, jfloat maxDelayTime, jfloat delayTime, jfloat feedBack){ + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::AudioBusId in_bus = m_engine->audioBusId().alloc(); + Methcla::AudioBusId out_bus = m_engine->audioBusId().alloc(); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_DELAY_URI, + Methcla::NodePlacement::after(m_nodeId[0]), + {delayTime, feedBack}, + {Methcla::Value(maxDelayTime)} + ); + + request.mapOutput(m_nodeId[0], 0, in_bus); + request.mapInput(synth.id(), 0, in_bus); + request.mapOutput(synth.id(), 0, out_bus); + request.mapInput(m_nodeId[1], 0, out_bus); + + request.activate(synth.id()); + + request.closeBundle(); + request.send(); + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return synth.id(); +}; + + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_delaySet(JNIEnv *env, jobject object, jfloat delayTime, jfloat feedBack, jint nodeId){ + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + request.set(nodeId, 0, delayTime); + request.set(nodeId, 1, feedBack); + request.closeBundle(); + request.send(); +}; + + + + +JNIEXPORT jlong JNICALL Java_processing_sound_MethClaInterface_amplitude(JNIEnv *env, jobject object, jintArray nodeId){ + + jlong ptr; + + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + + Methcla::AudioBusId in_bus = m_engine->audioBusId().alloc(); + Methcla::AudioBusId out_bus = m_engine->audioBusId().alloc(); + + Methcla::Request request(engine()); + + ServerValue *amp_ptr = (ServerValue *) malloc(sizeof(ServerValue)); + + ptr = (jlong)amp_ptr; + + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_AMPLITUDE_FOLLOWER_URI, + Methcla::NodePlacement::after(m_nodeId[0]), + {}, + {} + ); + + request.mapOutput(m_nodeId[0], 0, in_bus); + request.mapInput(synth.id(), 0, in_bus); + request.mapOutput(synth.id(), 0, out_bus); + request.mapInput(m_nodeId[1], 0, out_bus); + + request.activate(synth.id()); + request.closeBundle(); + request.send(); + + auto id = engine().addNotificationHandler([amp_ptr](const OSCPP::Server::Message& msg) { + if (msg == "/amplitude") { + OSCPP::Server::ArgStream args(msg.args()); + while (!args.atEnd()) { + + amp_ptr->amp = args.float32(); + } + return false; + } + return false; + }); + + amp_ptr->id = id; + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return ptr; +}; + +JNIEXPORT jfloat JNICALL Java_processing_sound_MethClaInterface_poll_1amplitude(JNIEnv * env, jobject object, jlong ptr){ + + ServerValue *amp_ptr = (ServerValue*)ptr; + + return amp_ptr->amp; +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_destroy_1amplitude(JNIEnv *env, jobject object, jlong ptr){ + + ServerValue *amp_ptr = (ServerValue*)ptr; + engine().removeNotificationHandler(amp_ptr->id); + free(amp_ptr); + +}; + +JNIEXPORT jlong JNICALL Java_processing_sound_MethClaInterface_fft(JNIEnv *env, jobject object, jintArray nodeId, jint fftSize){ + + jlong ptr; + jint* m_nodeId = env->GetIntArrayElements(nodeId, 0); + ServerArray *fft_ptr = (ServerArray *) malloc(sizeof(ServerArray) + fftSize*(sizeof(std::vector))); + + std::cout << "1" << std::endl; + + fft_ptr->fft.resize(fftSize); + std::cout << "2" << std::endl; + + fft_ptr->fftSize=fftSize; + ptr = (jlong)fft_ptr; + + Methcla::Engine::NotificationHandler msg; + + Methcla::AudioBusId in_bus = m_engine->audioBusId().alloc(); + Methcla::AudioBusId out_bus = m_engine->audioBusId().alloc(); + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + + auto synth = request.synth( + METHCLA_PLUGINS_FFT_URI, + Methcla::NodePlacement::after(m_nodeId[0]), + {}, + {Methcla::Value(fftSize)} + ); + + request.mapOutput(m_nodeId[0], 0, in_bus); + request.mapInput(synth.id(), 0, in_bus); + request.mapOutput(synth.id(), 0, out_bus); + request.mapInput(m_nodeId[1], 0, out_bus); + + request.activate(synth.id()); + request.closeBundle(); + request.send(); + + auto id = engine().addNotificationHandler([fft_ptr](const OSCPP::Server::Message& msg) { + if (msg == "/fft") { + OSCPP::Server::ArgStream args(msg.args()); + int i=0; + while (!args.atEnd()) { + fft_ptr->fft[i] = args.float32(); + std::lock_guard guard(mutex); + i++; + } + return false; + } + return false; + }); + + fft_ptr->id = id; + + env->ReleaseIntArrayElements(nodeId, m_nodeId, 0); + + return ptr; +}; + +JNIEXPORT jfloatArray JNICALL Java_processing_sound_MethClaInterface_poll_1fft(JNIEnv *env, jobject object, jlong ptr){ + + ServerArray *fft_ptr = (ServerArray*)ptr; + + jfloatArray fft_mag = env->NewFloatArray(fft_ptr->fftSize); + + + jfloat *m_fft_mag = env->GetFloatArrayElements(fft_mag, NULL); + + for (int i = 0; i < fft_ptr->fftSize; ++i) + { + m_fft_mag[i]=fft_ptr->fft[i]; + } + + env->ReleaseFloatArrayElements(fft_mag, m_fft_mag, 0); + + return fft_mag; +}; + +JNIEXPORT void JNICALL Java_processing_sound_MethClaInterface_destroy_1fft(JNIEnv *env, jobject object, jlong ptr){ + ServerArray *fft_ptr = (ServerArray*)ptr; + engine().removeNotificationHandler(fft_ptr->id); + free(fft_ptr); +}; + + +/* OLD VARIABLE IN OUT FUNCTION +JNIEXPORT jint JNICALL Java_processing_sound_MethClaInterface_out(JNIEnv *env, jobject object, jint in, jint out, jfloatArray pos){ + + float* n_pos = (float *)env->GetFloatArrayElements(pos, 0); + std::vector control (in, 0.f); + + for (int i = 0; i < in; ++i){control[i]=n_pos[i];} + + Methcla::Request request(engine()); + request.openBundle(Methcla::immediately); + auto synth = request.synth( + METHCLA_PLUGINS_PAN2_URI, + engine().root(), + control, + {Methcla::Value(in), Methcla::Value(out)} + ); + //request.mapOutput(synth.id(), 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); + request.activate(synth.id()); + request.closeBundle(); + request.send(); + + env->ReleaseFloatArrayElements(pos, n_pos, 0); + + return synth.id(); +}; +*/ + diff --git a/java/libraries/sound/src/processing/sound/Amplitude.java b/java/libraries/sound/src/processing/sound/Amplitude.java new file mode 100644 index 000000000..157594f13 --- /dev/null +++ b/java/libraries/sound/src/processing/sound/Amplitude.java @@ -0,0 +1,36 @@ +package processing.sound; +import processing.core.*; + +public class Amplitude { + + PApplet parent; + private MethClaInterface m_engine; + private long ptr; + + public Amplitude(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void input(SoundObject input){ + ptr = m_engine.amplitude(input.returnId()); + } + + public float process(){ + return m_engine.poll_amplitude(ptr); + } + /* + public void stop(){ + m_engine.synthStop(m_nodeId); + } + + public int returnId(){ + return m_nodeId; + } + */ + public void dispose() { + m_engine.destroy_amplitude(ptr); + //m_engine.synthStop(m_nodeId); + } +} diff --git a/java/libraries/sound/src/processing/sound/AudioIn.java b/java/libraries/sound/src/processing/sound/AudioIn.java new file mode 100644 index 000000000..e1f231408 --- /dev/null +++ b/java/libraries/sound/src/processing/sound/AudioIn.java @@ -0,0 +1,80 @@ +package processing.sound; + +import processing.core.PApplet; + +public class AudioIn { + + PApplet parent; + private MethClaInterface m_engine; + private int[] m_nodeId = {-1,-1}; + private float m_freq = 0; + private float m_amp = 0; + private float m_add = 0; + private float m_pos = 0; + + public AudioIn(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void play(float freq, float amp, float add, float pos){ + m_freq=freq; m_amp=amp; m_add=add; m_pos=pos; + m_nodeId = m_engine.sawPlay(m_freq, m_amp, m_add, m_pos); + } + + public void play(float freq, float amp, float add){ + m_freq=freq; m_amp=amp; m_add=add; + m_nodeId = m_engine.sawPlay(m_freq, m_amp, m_add, m_pos); + } + + public void play(float freq, float amp){ + m_freq=freq; m_amp=amp; + m_nodeId = m_engine.sawPlay(m_freq, m_amp, m_add, m_pos); + } + + public void play(){ + m_nodeId = m_engine.sawPlay(m_freq, m_amp, m_add, m_pos); + } + + private void set(){ + m_engine.oscSet(m_freq, m_amp, m_add, m_pos, m_nodeId); + } + + public void set(float freq, float amp, float add, float pos){ + m_freq=freq; m_amp=amp; m_add=add; m_pos=pos; + this.set(); + } + + public void freq(float freq){ + m_freq=freq; + this.set(); + } + + public void amp(float amp){ + m_amp=amp; + this.set(); + } + + public void add(float add){ + m_add=add; + this.set(); + } + + public void pan(float pos){ + m_pos=pos; + this.set(); + } + + public void stop(){ + m_engine.synthStop(m_nodeId); + } + + public int[] returnId(){ + return m_nodeId; + } + + public void dispose() { + m_engine.synthStop(m_nodeId); + } +} diff --git a/java/libraries/sound/src/processing/sound/BPF.java b/java/libraries/sound/src/processing/sound/BPF.java new file mode 100644 index 000000000..109437f7c --- /dev/null +++ b/java/libraries/sound/src/processing/sound/BPF.java @@ -0,0 +1,60 @@ +package processing.sound; + +import processing.core.PApplet; + +public class BPF implements SoundObject{ + + PApplet parent; + private MethClaInterface m_engine; + private int m_nodeId; + private int[] m_m = {0,0}; + private float m_freq = 100; + private float m_res = 1; + + public BPF(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void play(SoundObject input, float freq, float res){ + m_freq=freq; m_res=res; + m_nodeId = m_engine.bandPassPlay(input.returnId(), m_freq, m_res); + } + + public void play(SoundObject input, float freq){ + m_freq=freq; + m_nodeId = m_engine.bandPassPlay(input.returnId(), m_freq, m_res); + } + + private void set(){ + m_engine.filterSet(m_freq, m_res, m_nodeId); + } + + public void set(float freq, float res){ + m_freq=freq; m_res=res; + this.set(); + } + + public void freq(float freq){ + m_freq=freq; + this.set(); + } + + public void res(float res){ + m_res=res; + this.set(); + } + + public int[] returnId(){ + return m_m; + } + + public void stop(){ + //m_engine.synthStop(m_nodeId); + } + + public void dispose() { + //m_engine.synthStop(m_nodeId); + } +} diff --git a/java/libraries/sound/src/processing/sound/Delay.java b/java/libraries/sound/src/processing/sound/Delay.java new file mode 100644 index 000000000..c3b47084d --- /dev/null +++ b/java/libraries/sound/src/processing/sound/Delay.java @@ -0,0 +1,66 @@ +package processing.sound; + +import processing.core.PApplet; + +public class Delay implements SoundObject{ + + PApplet parent; + private MethClaInterface m_engine; + private int m_nodeId; + private int[] m_m = {0,0}; + private float m_maxDelayTime = 2; + private float m_delayTime = 0; + private float m_feedBack = 0; + + public Delay(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void play(SoundObject input, float maxDelayTime, float delayTime, float feedBack){ + m_maxDelayTime=maxDelayTime; m_delayTime=delayTime; m_feedBack=feedBack; + m_nodeId = m_engine.delayPlay(input.returnId(), m_maxDelayTime, m_delayTime, m_feedBack); + } + + public void play(SoundObject input, float maxDelayTime, float delayTime){ + m_maxDelayTime=maxDelayTime; m_delayTime=delayTime; + m_nodeId = m_engine.delayPlay(input.returnId(), m_maxDelayTime, m_delayTime, m_feedBack); + } + + public void play(SoundObject input, float maxDelayTime){ + m_maxDelayTime=maxDelayTime; + m_nodeId = m_engine.delayPlay(input.returnId(), m_maxDelayTime, m_delayTime, m_feedBack); + } + + private void set(){ + m_engine.delaySet(m_delayTime, m_feedBack, m_nodeId); + } + + public void set(float delayTime, float feedBack){ + m_delayTime=delayTime; m_feedBack=feedBack; + this.set(); + } + + public void time(float delayTime){ + m_delayTime=delayTime; + this.set(); + } + + public void feedback(float feedBack){ + m_feedBack=feedBack; + this.set(); + } + + public int[] returnId(){ + return m_m; + } + + public void stop(){ + //m_engine.synthStop(m_nodeId); + } + + public void dispose() { + //m_engine.synthStop(m_nodeId); + } +} diff --git a/java/libraries/sound/src/processing/sound/Env.java b/java/libraries/sound/src/processing/sound/Env.java new file mode 100644 index 000000000..9e72f2c10 --- /dev/null +++ b/java/libraries/sound/src/processing/sound/Env.java @@ -0,0 +1,23 @@ +package processing.sound; +import processing.core.*; + +public class Env { + + PApplet parent; + MethClaInterface m_engine; + int m_nodeId; + + public Env (PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void play(SoundObject input, float attackTime, float sustainTime, float sustainLevel, float releaseTime){ + m_nodeId = m_engine.envelopePlay(input.returnId(), attackTime, sustainTime, sustainLevel, releaseTime); + } + + public void dispose(){ + //m_engine.synthStop(m_nodeId); + } +}; diff --git a/java/libraries/sound/src/processing/sound/FFT.java b/java/libraries/sound/src/processing/sound/FFT.java new file mode 100644 index 000000000..2c3516a5d --- /dev/null +++ b/java/libraries/sound/src/processing/sound/FFT.java @@ -0,0 +1,40 @@ +package processing.sound; +import processing.core.*; + +public class FFT { + + PApplet parent; + private MethClaInterface m_engine; + private long ptr; + + public FFT(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void input(SoundObject input, int fftSize){ + ptr = m_engine.fft(input.returnId(), fftSize); + } + + public void process(float[] value){ + float[] m_value = m_engine.poll_fft(ptr); + int num_samples = Math.min(value.length, m_value.length); + for(int i=0; i 1){ + throw new UnsupportedOperationException("Panning is not supported for stereo files"); + } + + m_pos=pos; + this.set(); + } + + public void rate(float rate){ + m_rate=rate; + this.set(); + } + + public void amp(float amp){ + m_amp=amp; + this.set(); + } + + public void add(float add){ + m_add=add; + this.set(); + } + + public void stop(){ + m_engine.synthStop(m_nodeId); + } + + public int[] returnId(){ + return m_nodeId; + } + + public void dispose() { + m_engine.synthStop(m_nodeId); + } +} diff --git a/java/libraries/sound/src/processing/sound/SoundObject.java b/java/libraries/sound/src/processing/sound/SoundObject.java new file mode 100644 index 000000000..12b74f8be --- /dev/null +++ b/java/libraries/sound/src/processing/sound/SoundObject.java @@ -0,0 +1,8 @@ +package processing.sound; + +public interface SoundObject { + + //public int numBusses=128; + //public int topBus=0; + public int[] returnId(); +} \ No newline at end of file diff --git a/java/libraries/sound/src/processing/sound/SqrOsc.java b/java/libraries/sound/src/processing/sound/SqrOsc.java new file mode 100644 index 000000000..da8b518cf --- /dev/null +++ b/java/libraries/sound/src/processing/sound/SqrOsc.java @@ -0,0 +1,90 @@ +package processing.sound; + +import processing.core.PApplet; + +public class SqrOsc implements SoundObject { + + PApplet parent; + MethClaInterface m_engine; + private int[] m_nodeId = {-1,-1}; + private float m_freq = 0; + private float m_amp = 0; + private float m_add = 0; + private float m_pos = 0; + + public SqrOsc(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void play(){ + //m_nodeId = m_engine.pulsePlay(m_freq, 0.5f, m_amp*2, m_add-1, m_pos); + m_nodeId = m_engine.sqrPlay(m_freq, m_amp, m_add-1, m_pos); + }; + + public void play(float freq, float amp, float add, float pos){ + m_freq=freq; m_amp=amp; m_add=add; m_pos=pos; + this.play(); + }; + + public void play(float freq, float amp, float add){ + m_freq=freq; m_amp=amp; m_add=add; + this.play(); + }; + + public void play(float freq, float amp){ + m_freq=freq; m_amp=amp; + this.play(); + }; + + public void play(float freq){ + m_freq=freq; + this.play(); + }; + + private void set(){ + if(m_nodeId[0] != -1 ) { + m_engine.oscSet(m_freq, m_amp, m_add, m_pos, m_nodeId); + } + } + + public void set(float freq, float amp, float add, float pos){ + m_freq=freq; m_amp=amp; m_add=add; m_pos=pos; + this.set(); + }; + + public void freq(float freq){ + m_freq=freq; + this.set(); + } + + public void amp(float amp){ + m_amp=amp; + this.set(); + } + + public void add(float add){ + m_add=add; + this.set(); + } + + public void pan(float pos){ + m_pos=pos; + this.set(); + } + + public void stop(){ + m_engine.synthStop(m_nodeId); + }; + + public int[] returnId(){ + return m_nodeId; + }; + + public void dispose(){ + m_engine.synthStop(m_nodeId); + }; +} + + diff --git a/java/libraries/sound/src/processing/sound/TriOsc.java b/java/libraries/sound/src/processing/sound/TriOsc.java new file mode 100644 index 000000000..ca166ae67 --- /dev/null +++ b/java/libraries/sound/src/processing/sound/TriOsc.java @@ -0,0 +1,82 @@ +package processing.sound; + +import processing.core.PApplet; + +public class TriOsc implements Oscillator{ + + PApplet parent; + private MethClaInterface m_engine; + private int[] m_nodeId = {-1,-1}; + private float m_freq = 0; + private float m_amp = 0; + private float m_add = 0; + private float m_pos = 0; + + public TriOsc(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void play(float freq, float amp, float add, float pos){ + m_freq=freq; m_amp=amp; m_add=add; m_pos=pos; + m_nodeId = m_engine.triPlay(m_freq, m_amp, m_add, m_pos); + } + + public void play(float freq, float amp, float add){ + m_freq=freq; m_amp=amp; m_add=add; + m_nodeId = m_engine.triPlay(m_freq, m_amp, m_add, m_pos); + } + + public void play(float freq, float amp){ + m_freq=freq; m_amp=amp; + m_nodeId = m_engine.triPlay(m_freq, m_amp, m_add, m_pos); + } + + public void play(){ + m_nodeId = m_engine.triPlay(m_freq, m_amp, m_add, m_pos); + } + + private void set(){ + if(m_nodeId[0] != -1 ) { + m_engine.oscSet(m_freq, m_amp, m_add, m_pos, m_nodeId); + } + } + + public void set(float freq, float amp, float add, float pos){ + m_freq=freq; m_amp=amp; m_add=add; m_pos=pos; + this.set(); + } + + public void freq(float freq){ + m_freq=freq; + this.set(); + } + + public void amp(float amp){ + m_amp=amp; + this.set(); + } + + public void add(float add){ + m_add=add; + this.set(); + } + + public void pan(float pos){ + m_pos=pos; + this.set(); + } + + public void stop(){ + m_engine.synthStop(m_nodeId); + } + + public int[] returnId(){ + return m_nodeId; + } + + public void dispose() { + m_engine.synthStop(m_nodeId); + } +} diff --git a/java/libraries/sound/src/processing/sound/WhiteNoise.java b/java/libraries/sound/src/processing/sound/WhiteNoise.java new file mode 100644 index 000000000..6bc7ec4ea --- /dev/null +++ b/java/libraries/sound/src/processing/sound/WhiteNoise.java @@ -0,0 +1,76 @@ +package processing.sound; +import processing.core.*; + +public class WhiteNoise implements SoundObject{ + + PApplet parent; + private MethClaInterface m_engine; + private int[] m_nodeId = {-1,-1}; + private float m_amp=0; + private float m_add=0; + private float m_pos=0; + + public WhiteNoise(PApplet theParent) { + this.parent = theParent; + parent.registerMethod("dispose", this); + m_engine = new MethClaInterface(); + } + + public void play(){ + m_nodeId = m_engine.whiteNoisePlay(m_amp, m_add, m_pos); + } + + public void play(float amp, float add, float pos){ + m_amp=amp; m_add=add; m_pos=pos; + this.play(); + } + + public void play(float amp, float add){ + m_amp=amp; m_add=add; + this.play(); + } + + public void play(float amp){ + m_amp=amp; + this.play(); + } + + private void set(){ + if(m_nodeId[0] != -1 ) { + m_engine.whiteNoiseSet(m_amp, m_add, m_pos, m_nodeId); + } + } + + public void set(float amp, float add, float pos){ + m_amp=amp; + this.set(); + } + + public void amp(float amp){ + m_amp=amp; + this.set(); + } + + public void add(float add){ + m_add=add; + this.set(); + } + + public void pan(float pos){ + m_pos=pos; + this.set(); + } + + public void stop(){ + m_engine.synthStop(m_nodeId); + } + + public int[] returnId(){ + return m_nodeId; + } + + public void dispose() { + m_engine.synthStop(m_nodeId); + } +}; + \ No newline at end of file