Ruby Processing

Here is my blog in which I will describe my experiments with ruby-processing, find out more about ruby-processing at:- https://github.com/jashkenas/ruby-processing compatible with processing-2.2.1 and https://github.com/monkstone/cf3ruby for my version of the cfdg DSL (context-free-art)

Monday, 26 January 2015

Cranky keyboard (unless you are a northern European) sound library example

# 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 when 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.
load_library :sound

include_package 'processing.sound'

# Define the number of samples
NUM_SOUNDS = 5
attr_reader :device, :file, :value

def setup
  size(640, 360)
  background(255)
  # Create a Sound renderer and an array of empty soundfiles
  @device = AudioDevice.new(self, 48_000, 32)
  @file = []
  @value = Array.new(3, 0)
  # 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.
  NUM_SOUNDS.times do |i|
    file << SoundFile.new(self, format('%d.aif', (i + 1)))
  end
end

def draw
  background(*value) # splat array values
end

def key_pressed
  defined = true
  case key
  when 'a'
    file[0].play(0.5, 1.0)
  when 's'
    file[1].play(0.5, 1.0)
  when 'd'
    file[2].play(0.5, 1.0)
  when 'f'
    file[3].play(0.5, 1.0)
  when 'g'
    file[4].play(0.5, 1.0)
  when 'h'
    file[0].play(1.0, 1.0)
  when 'j'
    file[1].play(1.0, 1.0)
  when 'k'
    file[2].play(1.0, 1.0)
  when 'l'
    file[3].play(1.0, 1.0)
  when 'ö'
    file[4].play(1.0, 1.0)
  when 'ä'
    file[0].play(2.0, 1.0)
  when 'q'
    file[1].play(2.0, 1.0)
  when 'w'
    file[2].play(2.0, 1.0)
  when 'e'
    file[3].play(2.0, 1.0)
  when 'r'
    file[4].play(2.0, 1.0)
  when 't'
    file[0].play(3.0, 1.0)
  when 'z'
    file[1].play(3.0, 1.0)
  when 'u'
    file[2].play(3.0, 1.0)
  when 'i'
    file[3].play(3.0, 1.0)
  when 'o'
    file[4].play(3.0, 1.0)
  when 'p'
    file[0].play(4.0, 1.0)
  when 'ü'
    file[1].play(4.0, 1.0)
  else
    defined = false # only set background color value, if key is defined
  end
  @value = [rand(0..255), rand(0..255), rand(0..255)] if defined
end

Sunday, 25 January 2015

Another sound library example translated to ruby-processing


The original sketch was a blank-screen here is lame attempt to make it a bit more interesting, note use of ruby-processings map1d convenience method in favour of vanilla processings map convenience method (map is used for a different function in ruby, and python and a host of other languages for that matter). This one of those sketches that needs to use jruby-complete (some permission thing according to headius).
#
# This is a sound file player.
# NB: requires jruby-complete to run
# either --nojruby flag or use config
#

load_library :sound
include_package 'processing.sound'

attr_reader :sound_file

def setup
  size 640, 360
  background 255
  no_stroke
  # Load a soundfile
  @sound_file = SoundFile.new(self, 'vibraphon.aiff')
  report_settings
  # Play the file in a loop
  sound_file.loop
end

def draw
  red = map1d(mouse_x, (0..width), (30..255))
  green = map1d(mouse_y, (height..0), (30..255))
  fill(red, green, 0, 100)
  ellipse(mouse_x, mouse_y, 10, 10)
  manipulate_sound
end

def manipulate_sound
  # Map mouse_x 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.
  sound_file.rate(map1d(mouse_x, (0..width), (0.25..4.0)))
  # Map mouse_y from 0.2 to 1.0 for amplitude
  sound_file.amp(map1d(mouse_y, (0..width), (0.2..1.0)))
  # Map mouse_y from -1.0 to 1.0 for left to right
  sound_file.pan(map1d(mouse_y, (0..height), (-1.0..1.0)))
end

def report_settings
  # These methods return useful infos about the file
  p format('SFSampleRate= %d Hz', sound_file.sample_rate)
  p format('SFSamples= %d samples', sound_file.frames)
  p format('SFDuration= %d seconds', sound_file.duration)
end

Testing the new processing sound library in ruby-processing

There is a new processing sound library for processing-3.0 (that also works with processing-2.0), I thought I would give it a run-out in ruby-processing:-
# 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

load_library :sound

include_package 'processing.sound'

# The number of oscillators
NUM_SINES = 5

# A for calculating the amplitudes
attr_reader :sine_volume, :sine_waves

def setup
  size 500, 500
  background 255
  no_stroke
  create_oscillators
end

def create_oscillators
  # Create the oscillators and amplitudes
  @sine_waves = []
  @sine_volume = []
  NUM_SINES.times do |i|
    # The overall amplitude shouldn't exceed 1.0
    # The ascending waves will get lower in volume the higher the frequency
    sine_volume << (1.0 / NUM_SINES) / (i + 1)
    # Create the Sine Oscillators and start them
    wav = SinOsc.new(self)
    wav.play
    sine_waves << wav
  end
end

def draw
  fill mouse_x, mouse_y, 0, 100
  ellipse(mouse_x, mouse_y, 10, 10)
  # Use mouse_y to get values from 0.0 to 1.0
  yoffset = (height - mouse_y) / height.to_f
  # Set that value logarithmically to 150 - 1150 Hz
  frequency = 1000**yoffset + 150
  # Use mouse_x from -0.5 to 0.5 to get a multiplier for detuning the
  # oscillators
  detune = mouse_x.to_f / width - 0.5
  # Set the frequencies, detuning and volume
  sine_waves.each_with_index do |wav, i|
    wav.freq(frequency * (i + 1 + i * detune))
    wav.amp(sine_volume[i])
  end
end

Thursday, 22 January 2015

Mirror, a Dan Shiffman video capture sketch

#
# Mirror
# by Daniel Shiffman.
#
# Each pixel from the video source is drawn as a rectangle with rotation
# based on brightness.

load_library :video
include_package 'processing.video'

# Size of each cell in the grid
CELL_SIZE = 20

# Variable for capture device
attr_reader :cols, :rows, :video

def setup
  size(640, 480)
  frameRate(30)
  @cols = width / CELL_SIZE
  @rows = height / CELL_SIZE
  color_mode(RGB, 255, 255, 255, 100)
  @video = Capture.new(self, width, height)
  # Start capturing the images from the camera
  video.start
  background(0)
end

def draw
  return unless video.available
  video.read
  video.load_pixels
  # Begin loop for columns
  cols.times do |i|
    # Begin loop for rows
    rows.times do |j|
      # Where are we, pixel-wise?
      x = i * CELL_SIZE
      y = j * CELL_SIZE
      # Reversing x to mirror the image
      loc = (video.width - x - 1) + y * video.width
      r = red(video.pixels[loc])
      g = green(video.pixels[loc])
      b = blue(video.pixels[loc])
      # Make a new color with an alpha component
      c = color(r, g, b, 75)
      # Code for drawing a single rect
      # Using translate in order for rotation to work properly
      push_matrix
      translate(x + CELL_SIZE / 2, y + CELL_SIZE / 2)
      # Rotation formula based on brightness
      rotate((2 * PI * brightness(c) / 255.0))
      rect_mode(CENTER)
      fill(c)
      no_stroke
      # Rects are larger than the cell for some overlap
      rect(0, 0, CELL_SIZE + 6, CELL_SIZE + 6)
      pop_matrix
    end
  end
end

Wednesday, 21 January 2015

Experimenting with refinements

Now unfortunately refinements are not yet supported with jruby-9.0.0.0 but I'm going report experiment here (with mri ruby 2.2.0) to replace ruby-processing monkey patched string with refinement. Thought to self I could probably use forwardable to tidy things up a bit.
# test refinement to replace monkey patching
module StringUtil
  refine String do

    def titleize
      underscore.humanize.gsub(/\b([a-z])/) { $1.capitalize }
    end

    def humanize
      gsub(/_id$/, '').gsub(/_/, ' ').capitalize
    end

    def camelize(first_letter_in_uppercase = true)
      if first_letter_in_uppercase
        gsub(/\/(.?)/) { '::' + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
      else
        first + camelize[1..-1]
      end
    end

    def underscore
      gsub(/::/, '/')
        .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
        .gsub(/([a-z\d])([A-Z])/, '\1_\2')
        .tr('-', '_')
        .downcase
    end
  end
end

# Using StringUtil
class Test
  using StringUtil

  def initialize(title)
    @title = title
  end

  def titleize
    @title.titleize
  end

  def humanize
    @title.humanize
  end
end

fred = Test.new('bloody_toad')
puts fred.humanize
puts fred.titleize
Output:-
Bloody toad
Bloody Toad

But perhaps we don't need refinements with use of 'forwardable'
require 'forwardable'

# Avoid the monkey patching of String for camelize
class CamelString
  extend Forwardable
  def_delegators(:@string, *String.public_instance_methods(false))
  def initialize(str)
    @string = str
  end

  def camelize(first_letter_in_uppercase = true)
    if first_letter_in_uppercase
      @string.gsub(/\/(.?)/) { '::' + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
    else
      @string[0] + camelize[1..-1]
    end
  end
end

test = 'test_case'
puts CamelString.new(test).camelize
puts CamelString.new(test).camelize false

Output:-
TestCase
testCase

require 'forwardable'

# Avoid the monkey patching of String for underscore/titleize/humanize
class StringExtra
  extend Forwardable
  def_delegators(:@string, *String.public_instance_methods(false))
  def initialize(str)
    @string = str
  end

  def titleize
    gsub(/::/, '/')
      .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
      .gsub(/([a-z\d])([A-Z])/, '\1_\2')
      .tr('-', '_')
      .downcase
      .gsub(/_id$/, '')
      .gsub(/_/, ' ').capitalize
      .gsub(/\b([a-z])/) { $1.capitalize }
  end

  def humanize
    gsub(/_id$/, '').gsub(/_/, ' ').capitalize
  end

  def underscore
    gsub(/::/, '/')
      .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
      .gsub(/([a-z\d])([A-Z])/, '\1_\2')
      .tr('-', '_')
      .downcase
  end
end

test = 'TestCase'
puts StringExtra.new(test).underscore
puts StringExtra.new(test).titleize
puts StringExtra.new(test).humanize

Testcase
test_case
Test Case

What is more using these two classes I can completely replace the monkey-patching of String in JRubyArt and hence probably ruby-processing................

Sunday, 18 January 2015

Configuring ruby processing

There is this gui for configuring ruby-processing, it is a regular processing sketch you can run in the processing ide.
You should click the noruby button if you have not installed jruby (and do not intend to install jruby) on your system (but then you may find you can't run sketches using other gems).
You could use processing-3.0a5 instead of processing-2.2.1 (but then use the processing ide to install the minim and video libraries, since these are no longer included in processing-3.0).
The suggested location of processing root will adapt for your OS (a typical linux location is shown, Archlinux uses /usr/share/processing, others may choose /opt/processing).
Vanilla processing sketch configRP5.pde

This is an example config file:-
{
  "JRUBY": "false",
  "PROCESSING_ROOT": "/home/tux/processing-3.0a5",
  "X_OFF": 192,
  "Y_OFF": 108
}






Or create your own .rp5rc 'yaml' file in $HOME, with an editor such as vim (configRP5 saves as 'json', psych can use either form) X_OFF and Y_OFF can be used to control the position of the sketch on screen:-
---
PROCESSING_ROOT: /home/tux/processing-2.2.1
JRUBY: "false"
X_OFF: 50
Y_OFF: 50

Windows users might benefit from setting the 'RP5_ROOT' environmental variable here (point it to where gem gets installed). You can also enter global 'java_args' here if you wish, however using java_args.txt in the sketches data folder is more flexible. Though apparently a .jrubyrc in a local folder, takes precedence, this may be very useful to run say shader sketches (a possible alternative to my custom Rakefile to configure autorun sketches?). Anyone using processing-3.0+ should be aware that sketchbook path setting might be changed for processing-3.0 (sketchbook path is required for the auto-load of libraries eg video and minim).

Friday, 16 January 2015

A Golan Levin video capture sketch translate to ruby-processing

#
# Background Subtraction
# by Golan Levin.
# translated to ruby-processing by Martin Prout
# Detect the presence of people and objects in the frame using a simple
# background-subtraction technique. To initialize the background, press a key.
#

load_library :video
include_package 'processing.video'

attr_reader :background_pixels, :number_of_pixels, :video

def setup
  size(640, 480)
  init_video
end

def init_video
  # This the default video input, see the test_capture
  # example if it creates an error
  @video = Capture.new(self, width, height)
  # Start capturing the images from the camera
  video.start
  # Create array to store the background image
  @number_of_pixels = video.width * video.height
  @background_pixels = Array.new(number_of_pixels, 0)
  # Make the pixels[] array available for direct manipulation
  load_pixels
end

def capture  # captureEvent does not work like vanilla processing
  @video.read
  background 0
end

def draw
  return unless (video.available == true)
  capture
  video.load_pixels # Make the pixels of video available
  # Difference between the current frame and the stored background
  # current_sum = 0
  number_of_pixels.times do |i| # For each pixel in the video frame...
    # Fetch the current color in that location, and also the color
    # of the background in that spot
    curr_color = video.pixels[i]
    bkgd_color = background_pixels[i]
    # Extract the red, green, and blue components of the current pixel's color
    curr_r = curr_color >> 16 & 0xff
    curr_g = curr_color >> 8 & 0xff
    curr_b = curr_color & 0xff
    # Extract the red, green, and blue of the background pixel's color
    bkgd_r = bkgd_color >> 16 & 0xff
    bkgd_g = bkgd_color >> 8 & 0xff
    bkgd_b = bkgd_color & 0xff
    # Compute the difference of the red, green, and blue values
    diff_r = (curr_r - bkgd_r).abs
    diff_g = (curr_g - bkgd_g).abs
    diff_b = (curr_b - bkgd_b).abs
    # Add these differences to the running tally
    # current_sum += diff_r + diff_g + diff_b
    # Render the difference image to the screen
    pixels[i] = (diff_r << 16) | (diff_g << 8) | diff_b
  end
  update_pixels # Notify that the pixels[] array has changed
  # p current_sum # Print out the total amount of movement
end

def key_pressed
  video.load_pixels
  @background_pixels = video.pixels.clone
end

Followers

Blog Archive

About Me

My Photo
I am currently the lead developer of ruby-processing.