Processing Android – Playing Sounds

How to play sounds on Android, using Processing Android.

The minim library doesn’t work on Android, so use APWidgets instead.

Example:

import apwidgets.*;
APMediaPlayer player;

void setup()
{
  player = new APMediaPlayer(this);

  // SOUND FILE IS LOCATED IN THE '/data' FOLDER
  player.setMediaFile("sound.mp3");
}

void keyPressed()
{
  if (key == 's') player.start();
}

Resources:
http://forum.processing.org/one/topic/audio-for-android.html
https://code.google.com/p/apwidgets/


Processing Android – Signing an Android app

How to sign an Android app, generated with Processing Android (or generated with another tool)?

Create a (Windows-) batch file (.bat) with the following code:

@echo off
ECHO.
set /p varSketchName="Please enter the Name of your sketch: "
ECHO.
set /p varSketchAlias="Please enter an Alias for your sketch: "
ECHO.
ECHO [KEYTOOL]
keytool -genkey -v -keystore %~dp0%varSketchName%-release-key.keystore -alias %varSketchAlias% -keyalg RSA -keysize 2048 -validity 10000
pause
ECHO [ANT RELEASE]
call ant release
pause
ECHO [JARSIGNER]
call jarsigner -verbose -keystore %~dp0%varSketchName%-release-key.keystore %~dp0bin\%varSketchName%-release-unsigned.apk %varSketchAlias%
pause
ECHO [JARSIGNER VERIFY]
call jarsigner -verify %~dp0bin\%varSketchName%-release-unsigned.apk
pause
ECHO [ZIPALIGN]
set /p varSignedAppName="Please enter name for final signed apk (w/o .apk extension): "
call zipalign -v 4 %~dp0bin\%varSketchName%-release-unsigned.apk %~dp0%varSignedAppName%.apk

Programs you need:

  • keytool.exe (part of the Java JDK)
  • ant.bat (part of Apache ant)
  • jarsigner.exe (part of the Java JDK)
  • zipalign.exe (part of the Android SDK)

Steps:

  1. In Processing: create your sketch in Android Mode
  2. Export the sketch as an Android Project (Ctrl-Shift-E)
  3. Copy the .bat-file above to the newly created android directory and run it
  4. Name of your sketch = name of the .pde-file (without .pde!)
  5. Alias = same as the name (or something else)
  6. Enter a password for your keystore
  7. Enter your personal data
  8. Enter the same keystore password again
  9. Enter the name of the final .apk-file (without the .apk extension!)

N.B. Make sure your folder names DON’T include any SPACES! That will break the batch file.

And there you go! You’ve got a signed .apk file!


Processing Android – Wakelock (Keep Screen On)

Set the Android WAKELOCK (Phone won’t go into power save mode)
You’re sketch will need the ‘WAKE_LOCK‘ permission!

// For WAKELOCK
import android.os.Bundle; 
import android.view.WindowManager;

/*****************************************************************************************
 *
 *   SET WAKELOCK (PHONE WON'T GO INTO POWER SAVE MODE)
 * 
 *****************************************************************************************/
void onCreate(Bundle bundle)
{ 
  super.onCreate(bundle);
  getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} // onCreate()

Resources:
https://forum.processing.org/two/discussion/10486/how-do-i-get-the-android-view-for-my-sketch

Tested up to: Processing v3.1.2


Processing – Button Class

Button class for Java, Android and ProcessingJS modes:

Button btn;
btn = new Button(0, 0, btnWidth, btnHeight, color(255), color(255, 0, 0), "BUTTON");

/*****************************************************************************************
 * 
 *   BUTTON CLASS
 * 
 ****************************************************************************************/
class Button
{
  int x, y, w, h;
  color c;
  color cOver;
  String txt;
  int txtSize = 12;

  /****************************************************************************
   
   CONSTRUCTOR
   
   ****************************************************************************/
  Button (int _x, int _y, int _w, int _h, color _c, color _cover, String _txt)
  {
    x = _x;
    y = _y;
    w = _w;
    h = _h;
    c = _c;
    cOver = _cover;
    txt = _txt;
  }

  /****************************************************************************
   
   DISPLAY THE BUTTON
   
   ****************************************************************************/
  void display()
  {
    pushStyle();
    textAlign(CENTER);
    if (mouseOver())
      fill(cOver);
    else
      fill(c);
    stroke(100);
    strokeWeight(2);
    rect(x, y, w, h, 10);
    fill(0);
    textSize(txtSize);
    text(txt, x+w/2, y+h/2+txtSize/2);
    popStyle();
  }


  /****************************************************************************
   
   CHANGE THE TEXT ON THE BUTTON
   
   ****************************************************************************/
  void setText (String _txt)
  {
    txt = _txt;
    display();
  }

  /****************************************************************************
   
   IS THE MOUSE OVER THE BUTTON?
   
   ****************************************************************************/
  boolean mouseOver()
  {
    return (mouseX >= x && mouseX <= (x + w) && mouseY >= y && mouseY <= (y + h));
  }
} // Button

Calculations – MIDI Calculations

For use with the MIDI (Musical Instrument Digital Interface) standard, a frequency mapping is defined by:

p = 69 + 12 \times \log_2{f \over 440 \,\text{Hz}}

In Processing:

// log2(x) = log(x) / log(2);
final static float LOG2 = log(2);

int freqToPitch(float freq)
{
  return int(69+12*log(freq/440)/LOG2);
} // freqToPitch()

Where p is the MIDI note number. And in the opposite direction, to obtain the frequency from a MIDI note p, the formula is defined as:

f=2^{(p-69)/12} \times 440\,\text{Hz}

In Processing:

float pitch2Freq (int p)
{
  return pow(2,(float)(p-69)/12) * 440;
} // pitch2Freq()

For notes in an A440 equal temperament, this formula delivers the standard MIDI note number (p). Any other frequencies fill the space between the whole numbers evenly. This allows MIDI instruments to be tuned very accurately in any microtuning scale, including non-western traditional tunings.

midinotes

Calculate the note name from a note pitch (in Processing):

final static String[] names = {
  "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"
};

void setup()
{
  println(pitch2Name(90));
} // setup()

String pitch2Name (int pitch)
{
  return names[pitch%12]+((pitch/12)-1);
} // pitch2Name()

=> "Gb6"

Instrument groups:

  1. Piano
  2. Chromatic Percussion
  3. Organ
  4. Guitar
  5. Bass
  6. String
  7. Ensemble
  8. Brass
  9. Reed
  10. Pipe
  11. Synth Lead
  12. Synth Pad
  13. Synth Effects
  14. Ethnic
  15. Percussive
  16. Sound Effects

Resources:
http://en.wikipedia.org/wiki/Note

MIDI Note Number to Frequency Conversion Charts:
http://subsynth.sourceforge.net/midinote2freq.html
http://www.phys.unsw.edu.au/jw/notes.html

MIDI Instrument Map:
http://en.wikipedia.org/wiki/General_MIDI

MIDI Android Library:
https://code.google.com/p/android-midi-lib/


Processing – Crop Image Class

A Processing class for dynamically cropping an image.
For Java, Android and ProcessingJS modes:

Snippet:

/*****************************************************************************************
 * 
 *   CROPPER CLASS
 * 
 ****************************************************************************************/
public class Cropper
{
  // START POSITION: CENTER OF THE SCREEN
  float   x;
  float   y;

  float   xOffset = 0.0; 
  float   yOffset = 0.0;

  // MINIMUM SIZE OF THE CROP
  int     minWidth;
  int     minHeight;  

  boolean locked  = false;
  boolean enabled = false;


  /***************************************************************************************
   * 
   *   CONSTRUCTOR
   * 
   **************************************************************************************/
  Cropper(float _x, float _y, int _minWidth, int _minHeight)
  {
    x         = _x;
    y         = _y;
    minWidth  = _minWidth;
    minHeight = _minHeight;
  } // Cropper()


  /***************************************************************************************
   * 
   *   DISPLAY THE CROPPING FRAME
   * 
   **************************************************************************************/
  void display()
  {
    pushStyle();

    rectMode(RADIUS);
    strokeWeight(2);
    fill(255, 80);

    // IS ONE OF THE HANDLES BEING DRAGGED?
    boolean handleActive = false;

    // FIND THE ACTIVE HANDLE (=THE HANDLE BEING DRAGGED) AND MARK IT
    for (int i=0; i<4; i++) if (handles[i].locked) handleActive = true;

    if (handleActive)
    {
      // ONE OF THE HANDLES IS BEING DRAGGED
      stroke(255, 255, 0);
    } else
    {
      // CHANGE THE COLOR OF THE CROPPING FRAME, DEPENDING ON MOUSE OVER AND LOCKED STATE
      if (mouseOver())
      {
        if (!locked) stroke(255, 0, 0);
        else stroke(0, 0, 255);
      }
    }

    // DISPLAY CROPPING FRAME
    rect(x, y, cropperSizeDiv2, cropperSizeDiv2);

    // DISPLAY HANDLES
    // UPPER LEFT
    handles[0].display(x-cropperSizeDiv2+handleSizeDiv2, y-cropperSizeDiv2+handleSizeDiv2);    
    // UPPER RIGHT
    handles[1].display(x+cropperSizeDiv2-handleSizeDiv2, y-cropperSizeDiv2+handleSizeDiv2);
    // LOWER LEFT
    handles[2].display(x-cropperSizeDiv2+handleSizeDiv2, y+cropperSizeDiv2-handleSizeDiv2);
    // LOWER RIGHT
    handles[3].display(x+cropperSizeDiv2-handleSizeDiv2, y+cropperSizeDiv2-handleSizeDiv2);    

    popStyle();
  } // display()


  /***************************************************************************************
   * 
   *   MOUSE IS BEING DRAGGED
   * 
   **************************************************************************************/
  void mouseDragged()
  {     
    if (locked)
    { 
      x = mouseX - xOffset;
      y = mouseY - yOffset;
    }
  } // mouseDragged()


  /***************************************************************************************
   * 
   *   IS THE MOUSE OVER THE CROPPER?
   * 
   **************************************************************************************/
  boolean mouseOver()
  {
    return (mouseX > x-cropperSizeDiv2 && mouseX < x+cropperSizeDiv2
      && mouseY > y-cropperSizeDiv2 && mouseY < y+cropperSizeDiv2);
  } // mouseOver()
} // Cropper


/*****************************************************************************************
 * 
 *   HANDLE CLASS: THE HANDLES FOR CHANGING THE SIZE OF THE CROP
 * 
 ****************************************************************************************/
class Handle
{
  float   x, y;
  float   xOffset = 0.0; 
  float   yOffset = 0.0;
  boolean locked  = false;


  /***************************************************************************************
   * 
   *   CONSTRUCTOR
   * 
   **************************************************************************************/
  Handle()
  {
  } // Handle()


  /***************************************************************************************
   * 
   *   DISPLAY THE HANDLE
   * 
   **************************************************************************************/
  void display(float _x, float _y)
  {
    x = _x;
    y = _y;

    // IS ONE OF THE HANDLES BEING DRAGGED?
    boolean handleActive = false;
    for (int i=0; i<4; i++)
    {
      if (handles[i].locked) handleActive = true;
    }
    if (handleActive)
      // ONE OF THE HANDLES IS BEING DRAGGED: COLOR ALL HANDLES YELLOW
      fill(255, 255, 0);
    else    
      fill(0, 0, 255);

    noStroke();

    // DRAW HANDLE
    rect(x, y, handleSizeDiv2, handleSizeDiv2);
  } // display()


  /***************************************************************************************
   * 
   *   HANDLE IS BEING DRAGGED
   * 
   **************************************************************************************/
  void mouseDragged(int nr)
  {
    if (locked)
    { // THIS IS THE ACTIVE HANDLE (BEING DRAGGED)
      if (nr==0 || nr==2)
      { // UPPER LEFT OR LOWER LEFT
        xOffset = pmouseX-mouseX;
      } else
      { // UPPER RIGHT OR LOWER RIGHT
        xOffset = mouseX-pmouseX;
      }

      if (nr==0 || nr==1)
      { // UPPER LEFT OR UPPER RIGHT
        yOffset = pmouseY-mouseY;
      } else
      { // LOWER LEFT OR LOWER RIGHT
        yOffset = mouseY-pmouseY;
      }

      x = mouseX;     
      y = mouseY;

      cropperSizeDiv2 += xOffset;
      cropperSizeDiv2 += yOffset;

      // CONSTRAIN THE SIZE OF THE CROPPER
      if (cropperSizeDiv2 < thumbSizeDiv2) cropperSizeDiv2 = thumbSizeDiv2;
      if (cropperSizeDiv2 > (window.innerWidth>>1)) cropperSizeDiv2 = (window.innerWidth>>1);
    } // if (locked)
  } // mouseDragged()


  /***************************************************************************************
   * 
   *   IS THE MOUSE OVER THIS HANDLE?
   * 
   **************************************************************************************/
  boolean mouseOver()
  {
    return (mouseX > x-handleSizeDiv2 && mouseX < x+handleSizeDiv2
      && mouseY > y-handleSizeDiv2 && mouseY < y+handleSizeDiv2);
  } // mouseOver()
} // Handle

Resources:
SjansMachine 4.0 (‘Selfie’-version)