Skip to content

Commit 5f40a08

Browse files
committed
Add minim examples
1 parent c5d8185 commit 5f40a08

File tree

26 files changed

+522
-9
lines changed

26 files changed

+522
-9
lines changed

contributed/recursive_pentagon.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def settings
3030

3131
RecursivePentagons.new
3232

33-
# Here we include Processing::Proxy to mimic vanilla processing inner class
33+
# Here we include Propane::Proxy to mimic vanilla processing inner class
3434
# access.
3535
class PentagonFractal
3636
include Propane::Proxy

external_library/java/minim/Rakefile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Simple demo Rakefile to autorun samples in current directory
2+
3+
desc 'run demo'
4+
task default: [:demo]
5+
6+
desc 'demo'
7+
task :demo do
8+
samples_list.shuffle.each { |sample| run_sample sample }
9+
end
10+
11+
def samples_list
12+
Dir.glob('*.rb').map { |file| File.join(__dir__, file) }
13+
end
14+
15+
def run_sample(sample_name)
16+
puts "Running #{sample_name}...quit to run next sample"
17+
system "jruby --dev #{sample_name}"
18+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env jruby
2+
# frozen_string_literal: true
3+
4+
require 'propane'
5+
6+
# ADSRExample is an example of using the ADSR envelope within an instrument.
7+
# For more information about Minim and additional features,
8+
# visit http://code.compartmental.net/minim/
9+
# author: Anderson Mills<br/>
10+
# Anderson Mills's work was supported by numediart (www.numediart.org)
11+
class ADSRExample < Propane::App
12+
load_libraries :minim, :tone_instrument
13+
java_import 'ddf.minim.Minim'
14+
15+
attr_reader :out
16+
17+
def setup
18+
sketch_title 'ADSR Example'
19+
minim = Minim.new(self)
20+
@out = minim.get_line_out(Minim::MONO, 2048)
21+
# pause time when adding a bunch of notes at once
22+
out.pause_notes
23+
# make four repetitions of the same pattern
24+
4.times do |i|
25+
# add some low notes
26+
out.play_note(1.25 + i * 2.0, 0.3, ToneInstrument.new(out, 75, 0.49))
27+
out.play_note(2.50 + i * 2.0, 0.3, ToneInstrument.new(out, 75, 0.49))
28+
29+
# add some middle notes
30+
out.play_note(1.75 + i * 2.0, 0.3, ToneInstrument.new(out, 175, 0.4))
31+
out.play_note(2.75 + i * 2.0, 0.3, ToneInstrument.new(out, 175, 0.4))
32+
33+
# add some high notes
34+
out.play_note(1.25 + i * 2.0, 0.3, ToneInstrument.new(out, 3750, 0.07))
35+
out.play_note(1.5 + i * 2.0, 0.3, ToneInstrument.new(out, 1750, 0.02))
36+
out.play_note(1.75 + i * 2.0, 0.3, ToneInstrument.new(out, 3750, 0.07))
37+
out.play_note(2.0 + i * 2.0, 0.3, ToneInstrument.new(out, 1750, 0.02))
38+
out.play_note(2.25 + i * 2.0, 0.3, ToneInstrument.new(out, 3750, 0.07))
39+
out.play_note(2.5 + i * 2.0, 0.3, ToneInstrument.new(out, 5550, 0.09))
40+
out.play_note(2.75 + i * 2.0, 0.3, ToneInstrument.new(out, 3750, 0.07))
41+
end
42+
# resume time after a bunch of notes are added at once
43+
out.resumeNotes
44+
end
45+
46+
# draw is run many times
47+
def draw
48+
# erase the window to black
49+
background(0)
50+
# draw using a white stroke
51+
stroke(255)
52+
# draw the waveforms
53+
(0...(out.bufferSize - 1)).each do |i|
54+
# find the x position of each buffer value
55+
x1 = map1d(i, 0..out.bufferSize, 0..width)
56+
x2 = map1d(i + 1, 0..out.bufferSize, 0..width)
57+
# draw a line from one buffer position to the next for both channels
58+
line(x1, 50 + out.left.get(i) * 50, x2, 50 + out.left.get(i + 1) * 50)
59+
line(x1, 150 + out.right.get(i) * 50, x2, 150 + out.right.get(i + 1) * 50)
60+
end
61+
end
62+
63+
def settings
64+
size(512, 200, P2D)
65+
end
66+
end
67+
68+
ADSRExample.new
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env jruby
2+
# frozen_string_literal: true
3+
4+
require 'propane'
5+
# This sketch demonstrates how to create synthesized sound with Minim
6+
# using an AudioOutput and an Instrument we define. By using the
7+
# playNote method you can schedule notes to played at some point in the
8+
# future, essentially allowing to you create musical scores with code.
9+
# Because they are constructed with code, they can be either
10+
# deterministic or different every time. This sketch creates a
11+
# deterministic score, meaning it is the same every time you run the
12+
# sketch. For more complex examples of using playNote check out
13+
# algorithmicCompExample and compositionExample. For more information
14+
# about Minim and additional features, visit
15+
# http://code.compartmental.net/minim/
16+
class CreateInstrument < Propane::App
17+
load_libraries :minim, :sine_instrument
18+
java_import 'ddf.minim.Minim'
19+
20+
attr_reader :out
21+
22+
def settings
23+
size(512, 200, P2D)
24+
end
25+
26+
def setup
27+
sketch_title 'Create Instrument'
28+
minim = Minim.new(self)
29+
@out = minim.getLineOut
30+
# when providing an Instrument, we always specify start time and duration
31+
out.playNote(0.0, 0.9, SineInstrument.new(out, 97.99))
32+
out.playNote(1.0, 0.9, SineInstrument.new(out, 123.47))
33+
# we can use the Frequency class to create frequencies from pitch names
34+
out.playNote(2.0, 2.9, SineInstrument.new(out, Frequency.ofPitch('C3').asHz))
35+
out.playNote(3.0, 1.9, SineInstrument.new(out, Frequency.ofPitch('E3').asHz))
36+
out.playNote(4.0, 0.9, SineInstrument.new(out, Frequency.ofPitch('G3').asHz))
37+
end
38+
39+
def draw
40+
background(0)
41+
stroke(255)
42+
# draw the waveforms
43+
(out.bufferSize - 1).times do |i|
44+
line(i, 50 + out.left.get(i) * 50, i + 1, 50 + out.left.get(i + 1) * 50)
45+
line(i, 150 + out.right.get(i) * 50, i + 1, 150 + out.right.get(i + 1) * 50)
46+
end
47+
end
48+
end
49+
50+
CreateInstrument.new
5.91 KB
Binary file not shown.
27.4 KB
Binary file not shown.
7.03 KB
Binary file not shown.
8.03 KB
Binary file not shown.
424 KB
Binary file not shown.
22 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env jruby
2+
# frozen_string_literal: true
3+
4+
require 'propane'
5+
# This sketch is a more involved use of AudioSamples to create a simple drum machine.
6+
# Click on the buttons to toggle them on and off. The buttons that are on will trigger
7+
# samples when the beat marker passes over their column. You can change the tempo by
8+
# clicking in the BPM box and dragging the mouse up and down.
9+
# We achieve the timing by using AudioOutput's playNote method and a cleverly written Instrument.
10+
# For more information about Minim and additional features,
11+
# visit http://code.compartmental.net/minim/
12+
class DrumMachine < Propane::App
13+
load_libraries :minim, :tick
14+
java_import 'ddf.minim.Minim'
15+
java_import 'ddf.minim.ugens.Sampler'
16+
attr_reader :minim, :out, :kick, :snare, :hat, :bpm, :buttons
17+
attr_reader :kikRow, :snrRow, :hatRow
18+
attr_accessor :beat
19+
def setup
20+
sketch_title 'Drum Machine'
21+
minim = Minim.new(self)
22+
@out = minim.getLineOut
23+
@hatRow = Array.new(16, false)
24+
@snrRow = Array.new(16, false)
25+
@kikRow = Array.new(16, false)
26+
@buttons = []
27+
@bpm = 120
28+
@beat = 0
29+
# load all of our samples, using 4 voices for each.
30+
# this will help ensure we have enough voices to handle even
31+
# very fast tempos.
32+
@kick = Sampler.new(data_path('BD.wav'), 4, minim)
33+
@snare = Sampler.new(data_path('SD.wav'), 4, minim)
34+
@hat = Sampler.new(data_path('CHH.wav'), 4, minim)
35+
# patch samplers to the output
36+
kick.patch(out)
37+
snare.patch(out)
38+
hat.patch(out)
39+
16.times do |i|
40+
buttons << Rect.new(10 + i * 24, 50, hatRow, i)
41+
buttons << Rect.new(10 + i * 24, 100, snrRow, i)
42+
buttons << Rect.new(10 + i * 24, 150, kikRow, i)
43+
end
44+
# start the sequencer
45+
out.setTempo(bpm)
46+
out.playNote(0, 0.25, Tick.new)
47+
end
48+
49+
def draw
50+
background(0)
51+
fill(255)
52+
# text(frameRate, width - 60, 20)
53+
buttons.each(&:draw)
54+
stroke(128)
55+
(beat % 4).zero? ? fill(200, 0, 0) : fill(0, 200, 0)
56+
# beat marker
57+
rect(10 + beat * 24, 35, 14, 9)
58+
end
59+
60+
def mouse_pressed
61+
buttons.each(&:mouse_pressed)
62+
end
63+
64+
def settings
65+
size(395, 200)
66+
end
67+
end
68+
69+
DrumMachine.new
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# frozen_string_literal: true
2+
3+
%w[Frequency Instrument Line Oscil Waves].each do |klass|
4+
java_import "ddf.minim.ugens.#{klass}"
5+
end
6+
7+
# to make an Instrument we must define a class
8+
# that includes Instrument interface (as a module).
9+
class SineInstrument
10+
include Instrument
11+
12+
attr_reader :wave, :amp_env, :out, :tone_instrument
13+
14+
def initialize(out, frequency)
15+
# make a sine wave oscillator
16+
# the amplitude is zero because
17+
# we are going to patch a Line to it anyway
18+
@out = out
19+
@wave = Oscil.new(frequency, 0, Waves::SINE)
20+
@amp_env = Line.new
21+
amp_env.patch(wave.amplitude)
22+
end
23+
24+
# this is called by the sequencer when this instrument
25+
# should start making sound. the duration is expressed in seconds.
26+
def noteOn(duration)
27+
# start the amplitude envelope
28+
amp_env.activate(duration, 0.5, 0)
29+
# attach the oscil to the output so it makes sound
30+
wave.patch(out)
31+
end
32+
33+
# this is called by the sequencer when the instrument should
34+
# stop making sound
35+
def noteOff
36+
wave.unpatch(out)
37+
end
38+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
# simple class for drawing the gui
4+
class Rect
5+
include Propane::Proxy
6+
attr_reader :x, :y, :w, :h, :steps, :stepId
7+
8+
def initialize(_x, _y, _steps, _id)
9+
@x = _x
10+
@y = _y
11+
@w = 14
12+
@h = 30
13+
@steps = _steps
14+
@stepId = _id
15+
end
16+
17+
def draw
18+
steps[stepId] ? fill(0, 255, 0) : fill(255, 0, 0)
19+
rect(x, y, w, h)
20+
end
21+
22+
def mouse_pressed
23+
if mouse_x >= x && mouse_x <= x + w && mouse_y >= y && mouse_y <= y + h
24+
steps[stepId] = !steps[stepId]
25+
end
26+
end
27+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
java_import 'ddf.minim.ugens.Instrument'
4+
# class Tick can access app variables by including Propane::App module
5+
# But we must use instance variable to set the beat
6+
class Tick
7+
include Instrument
8+
include Propane::Proxy
9+
10+
def noteOn(_dur)
11+
hat.trigger if hatRow[beat]
12+
snare.trigger if snrRow[beat]
13+
kick.trigger if kikRow[beat]
14+
end
15+
16+
def noteOff
17+
# next beat
18+
Propane.app.beat = (beat + 1) % 16
19+
# set the new tempo
20+
out.setTempo(bpm)
21+
# play this again right now, with a sixteenth note duration
22+
out.playNote(0, 0.25, self)
23+
end
24+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require_relative 'lib/tick'
2+
require_relative 'lib/rect'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
3+
# java_import 'ddf.minim.Minim'
4+
5+
%w[ADSR Instrument Oscil Waves].each do |klass|
6+
java_import "ddf.minim.ugens.#{klass}"
7+
end
8+
9+
# Every instrument must implement the Instrument interface so
10+
# playNote can call the instrument's methods.
11+
class ToneInstrument
12+
include Instrument
13+
attr_reader :adsr, :out
14+
15+
# constructor for this instrument NB: includes line_out
16+
def initialize(out, frequency, amplitude)
17+
# create new instances of any UGen objects as necessary
18+
sineOsc = Oscil.new(frequency, amplitude, Waves::TRIANGLE)
19+
@adsr = ADSR.new(0.5, 0.01, 0.05, 0.5, 0.5)
20+
@out = out
21+
# patch everything together up to the final output
22+
sineOsc.patch(adsr)
23+
end
24+
25+
# every instrument must have a noteOn method
26+
def noteOn(dur)
27+
# turn on the ADSR
28+
adsr.noteOn
29+
# patch to the output
30+
adsr.patch(out)
31+
end
32+
33+
# every instrument must have a noteOff method
34+
def noteOff
35+
# tell the ADSR to unpatch after the release is finished
36+
adsr.unpatchAfterRelease(out)
37+
# call the noteOff
38+
adsr.noteOff
39+
end
40+
end

external_library/java/minim/loop.rb

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env jruby
2+
# frozen_string_literal: true
3+
4+
require 'propane'
5+
# This sketch demonstrates how to use the loop method of a Playable class.
6+
# The class used here is AudioPlayer, but you can also loop an AudioSnippet.
7+
# When you call loop() it will make the Playable playback in an infinite loop.
8+
# If you want to make it stop looping you can call play() and it will finish the
9+
# current loop and then stop. Press 'l' to start the player looping.
10+
class Loop < Propane::App
11+
load_library :minim
12+
java_import 'ddf.minim.Minim'
13+
14+
attr_reader :groove
15+
16+
def settings
17+
size(512, 200, P2D)
18+
end
19+
20+
def setup
21+
sketch_title "Press \'l\' key to loop"
22+
minim = Minim.new(self)
23+
@groove = minim.load_file(data_path('groove.mp3'), 2048)
24+
end
25+
26+
def draw
27+
background(0)
28+
stroke(255)
29+
(0...groove.buffer_size - 1).each do |i|
30+
line(i, 50 + groove.left.get(i) * 50, i + 1, 50 + groove.left.get(i + 1) * 50)
31+
line(i, 150 + groove.right.get(i) * 50, i + 1, 150 + groove.right.get(i + 1) * 50)
32+
end
33+
end
34+
35+
def key_pressed
36+
return unless key == 'l'
37+
38+
groove.loop
39+
end
40+
end
41+
42+
Loop.new

0 commit comments

Comments
 (0)