Glimmer

Faint insights into (mostly) the world of automation

Java Sound Update 2

| Comments

This is an update to a prior article.

UAB EE Students Andrew Peturis, Chaselyn Langley and other team members produced an interesting GUI Application for children to associate sounds with images. In the course of doing this, they wished to expand the control of the audio beyond my original example. They discovered a bit of behavior that was not (at least clearly) documented in the JAVA API.

They modified the original demo substantially for their application and supplied a limited version of it to me as an update to benefit future classes (as well as the broader Internete Community). I merged their two classes into one focused only on the sound system behaviour and hopefully preserved the important behaviors they wished to document. The new code is show below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
 * File: Sound.java
 * Authors: Andrew Peturis, Chaselyn Langley, David Green
 * Vers: 1.1.0 12/10/2015 dgg - update example to standalone
 * Vers: 1.0.0 10/20/2015 agp - initial coding
 *
 * Credits:  Team SoundBox - EE333 Fall 2015
 */

import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class Sound {

    private String sound;
    private Clip clip;
    private boolean isPlaying;  // Sound loaded up and has started playing (may be paused)


    // Don't allow default constructor
    private Sound() {
    }

    public Sound(String sound) {
        this.sound = sound;
        isPlaying = false;
    }

    // Play a sound using javax.sound and Clip interface
    public void play() {
        try {
            if (!isPlaying) {
                // Create a clip to hold a sound
                clip = AudioSystem.getClip();
                clip.open(AudioSystem.getAudioInputStream(new File(sound)));
                isPlaying = true;
            }
            // Play the audio clip with the audioplayer class
            clip.start();
        } catch (LineUnavailableException | UnsupportedAudioFileException | IOException e ) {
            System.out.println("Things did not go well");
            System.exit(-1);
        }
    }

    // Continue a stopped sound
    public void resume() {
        if (isPlaying) {
            clip.start();
        }
    }

    // Pause a playing sound
    public void pause() {
        // clip.stop() will only pause the sound and still leave the sound in the line
        // waiting to be continued. It does not actually clear the line so a new action could be performed.
        if (isPlaying) {
            clip.stop();
        }
    }

    // Stop a sound from playing and clear out the line to play another sound if need be.
    public void end() {
        if (isPlaying) {
            pause();
            // clip.close(); will clear out the line and allow a new sound to play. clip.flush() was not 
            // used because it can only flush out a line of data already performed.
            clip.close();
            isPlaying = false;
        }
    }

    // Small demo program
    public static void main(String[] args) {
        Sound sound = new Sound("predator.wav");
        try {
            sound.play();
            System.out.println("Playing for 1 second");
            Thread.sleep(1000);     // let it play a second
            sound.pause();          // pause
            System.out.println("Pausing for 5 seconds");
            Thread.sleep(5000);     // continue playing
            System.out.println("Playing for 1 second");
            sound.resume();
            Thread.sleep(1000);      // play have a second
            System.out.println("Ending, resuming which should silently fail");
            sound.end();            // End sound
            sound.resume();         // Should be silent
            Thread.sleep(5000);     // Wait 5 seconds
            System.out.println("Playing again for 5 seconds");
            sound.play();           // Should start again
            Thread.sleep(5000);
            sound.end();
        } catch( InterruptedException e) {
            // just fall to end
            // System.exit(0);
        }
    }
}

Creating a Line Segment Graphic

| Comments

The following code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*
 * File: CreateSimplePlot.java
 * Author: David Green <dgreen@uab.edu>
 * Vers: 1.1.0 09/21/2015 dgg - change from Etch-A-Sketch to Plotter
 * Vers: 1.0.0 09/10/2014 dgg - initial coding
 *
 * Credits:  (if any for sections of code)
 */

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

/**
 * Code from:  http://www.java2s.com/Code/Java/2D-Graphics-GUI/DrawanImageandsavetopng.htm
 * (modifications by ...)
 * @author David Green <dgreen@uab.edu>
 */
public class CreateSimplePlot {

  static public void main(String args[]) throws Exception {

    double t;       // time
    double oldT;    // old time
    double deltaT;  // time increment
    double y;       // plot value
    double oldY;    // old plot value

    try {
      int width = 700, height = 500;

      // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
      // into integer pixels
      BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

      Graphics2D ig2 = bi.createGraphics();

      // make full image white
      ig2.fillRect(0, 0, 700, 500);

      // draw black lines
      ig2.setPaint(Color.BLACK);

      oldT = 0.;
      oldY = plotFunction(oldT);
      deltaT = 5.;


      for (t = deltaT; t < 700.; t += deltaT ) {
        y = 250.0 + 200.0 * Math.sin( (t / 200.) * 2. * Math.PI );
        ig2.drawLine((int) oldT, (int) oldY, (int) t, (int) y);
        oldY = y;
        oldT = t;
      }

      // write the resulting file
      ImageIO.write(bi, "PNG", new File("plot.png"));

    } catch (IOException ie) {
      System.out.println("Exception raised " + ie);
    }
  }

  private static double plotFunction(double t) {
    return 250.0 + 200.0 * Math.sin( (t / 200.) * 2. * Math.PI );
  }
}

produces

Figure 1. Sinusoid example plot

as a PNG graphic file.

Software Programming Skills Popularity

| Comments

There are lots of ways to evaluate popularity, and in the end, all are probably very transitory. In his Quartz article, Max Nisen examined American job data and reports the top ten slanguage/frameworks (by salary offers) are

  1. Ruby on Rails
  2. Objective C
  3. Python
  4. Java
  5. C++
  6. JavaScript
  7. C
  8. R
  9. C#
  10. Visual Basic

Java Sound Update

| Comments

For years, I have been using (and advising the use of sun.audio) for playing sounds in the Java VM using Alvin Alexander’s article for inspiration. The downside of this approach, of course, is the dependency on classes that are not part of the JAVA API and therefore might change at anytime although they have been relatively stable for many years.

Recently, I found a stackoverflow discussion noting the existence of AudioSystem (and related clases).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
 * File: ModernRingPhone.java
 * Author: David Green <dgreen@uab.edu>
 * Assignment:  SoundOff - EE333 Fall 2014
 * Vers: 1.0.0 10/23/2014 dgg - initial coding
 *
 * Credits:  Concepts from http://stackoverflow.com/questions/15475276/short-sound-file-plays-in-netbeans-but-not-in-jar
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

/**
 *
 * @author David Green <dgreen@uab.edu>
 */
public class ModernRingPhone {

  public static void main(String[] args)
  {
    try {
        // open the sound file as a Java input stream
        String gongFile = "telephone-ring-3.wav";

        // create an audiostream from the inputstream
        Clip clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(new File(gongFile)));

        // play the audio clip with the audioplayer class
        clip.start();

        // wait for sound to finish before terminating program (necessary for 
        // running inside NetBeans, at least)
        Thread.sleep(10000);
    }
    catch ( Exception e) {
        System.out.println("Things did not go well" );
        e.printStackTrace();
        System.exit(-1);
    }
  }
}

Update: 2015-02-08 Removed unnecessary line of code

Update: 2015-12-10 Revised example is available here.

Updating Java/NetBeans on OS X

| Comments

Oracle Java on OS X

After moving to Oracle’s Java to use on OS X, one finds a new System Preference pane which launches a Java preferences App. Included here is a preset option to update Java. However, one should note that if one installed the JDK (Java Development Kit) as opposed to the JRE (Java Run Time environment) only the JRE portion of the package is automatically updated. It appears one has to manually download and update the JDK to get that portion of the package updated. Investigating a bit, it looks like the attention is on the JRE and only the linkages for the Applet usage are updated automatically. The JDK and its command line version of java are not updated in addition to all the other components of the JDK. This is probably not all bad as it means the development environment does not change on you without notice.

NetBeans on OS X

To update NetBeans, download a later version, start the new version and agree to migration from the earlier release. Then, when you are ready to eliminate the old version of NetBeans, go to the /Applications/NetBeans directory and delete only that old version.

Update: 25 Jan 2014 — The template directory is made when the first template is created. On the Mac, it is located at ~/Library/Application\ Support/NetBeans/7.4/config/Templates and has a Classes directory and a Properties directory. It appears that one can move the templates from one config directory to a new one as part of migrating.

Using MySQL With Bitnami on Vagrant VM

| Comments

The Bitnami install doccumented earlier is a bit broken relative to MySQL. Evidently, when Bitnami installs in the VM situation and moves itself to the /opt/rubystack... directory, it expects to be a slightly more traditional install and use the userid mysql for running the MySQL server. However, the built databases are secured to the vagrant user account (at least in my release). This is relatively easy to confirm, try starting the server

 sudo ~/rubystack/ctlscript.sh start mysql

and not that it fails (after a bit). One can look at the mysqld.log in ~/rubystack/mysql/data directory and see access errors.

The fix is to give the mysql user access to the MySQL data directory:

cd ~/rubystack/mysql  
sudo chown -R mysql.vagrant data

After this is done, the startup command should work.

To start MySQL

cd rubystack  
sudo ./ctlscript.sh start mysql

To stop MySQL

cd rubystack  
sudo ./ctlscript.sh stop mysql

Create MySQL databases for dev, test, production

The following sequence setups the development, test, and production databases for the Rails project tbd4students.

tbd4students> mysql -u root
CREATE DATABASE tbd4students_development DEFAULT CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON tbd4students_development.* TO 'vagrant'@'localhost' IDENTIFIED BY 'password';
CREATE DATABASE tbd4students_test DEFAULT CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON tbd4students_test.* TO 'vagrant'@'localhost' IDENTIFIED BY 'password';
CREATE DATABASE tbd4students_production DEFAULT CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON tbd4students_production.* TO 'vagrant'@'localhost' IDENTIFIED BY 'password';
EXIT;

In this example, the username vagrant (default in the VM) with a password of ‘password’ is used to access the server. There is also a presumption that the root password to the MySQL server is blank. This configuration, of course, is NO security and is only useful when on a closed system. One should research how to configure things more securely for anything that will be Internet (or even LAN) visible.

Make a New Rails 3.2 App in the VM Environment

| Comments

After installing the VM on the computer, one can make a new Rails App by the following process:

  • Go to the machine directory in your host machine using the command line
  • Start up the VM if it is not running vagrant up
  • Connect to the machine vagrant ssh
  • First time only, tell git who you are

       git config --global user.email "you@example.com"
       git config --global user.name "Your Name"
    
  • Change to the directory that is shared between the guest OS and the host OS cd /vagrant

  • Create a new Rails application without running bundler rails new appname --skip-bundle
  • Install git’s tracking repository git init appname
  • Change to the application directory cd appname
  • Update .gitignore to skip tracking database.yml You can edit this on the host side rather than the OS side if you like your local editor better but use one that likes Unix line endings (i.e. NOT Notepad).
.gitignore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile ~/.gitignore_global

# Ignore bundler config
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3

# Ignore all logfiles and tempfiles.
/log/*.log
/tmp

# Ignore database.yml
/config/database.yml
  • Move the code to check in to the staging area git add .
  • Commit the code the repository git commit -m "Initial commit after ignoring database.yml"
  • Make a copy of database.yml for archival cp config/database.yml config/database-example.yml
  • Modify the Gemfile to include thin, theracer, and quiet_assets gems.
Gemfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
source 'https://rubygems.org'

gem 'rails', '3.2.13'

# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'

gem 'sqlite3'
gem 'thin'
gem 'quiet_assets', :group => :development


# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'

  # See https://github.com/sstephenson/execjs#readme for more supported runtimes
  gem 'therubyracer', :platforms => :ruby

  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails'

# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'

# To use Jbuilder templates for JSON
# gem 'jbuilder'

# Use unicorn as the app server
# gem 'unicorn'

# Deploy with Capistrano
# gem 'capistrano'

# To use debugger
# gem 'debugger'


  • Run bundle to lock in the gemfiles using the local version of the gems bundle install --local
  • Start up the Rails app rails s
  • On the host side, point the browser to http://192.168.33.10:3000 and ensure that the base Rails “Welcome aboard” is visible.

Updated 7 June 2013 after presentation to class — correcting two typos.

Using Bitnami’s Rubystack on VirtualBox Linux With Vagrant

| Comments

In my Software Engineering Projects class, we are going to do a RAILS-based project. Based on feedback from last year’s class, I am furnishing students with an almost configured RAILS system for development. The system is based on

Create the base machine

The Vagrantfile is configured to support networking from the host machine and to run a configuration script (script.sh) as part of the provisioning. There is a small bit of logic to avoid re-provisioning things each time the machine is brought up.

I make it by following roughly the following procedure:

  1. Install Virtualbox
  2. Install Vagrant
  3. Create a directory to place files add in
    • The bitnami linux installer file
    • Vagrantfile
    • script.sh
  4. Do an install: vagrant up
  5. SSH into the running host: vagrant ssh
  6. Use Linux as needed
  7. Exit back to the host OS: exit
  8. Shutdown the virtual machine (saving the contents): vagrant halt
  9. Package the machine post configuration with vagrant package default --output rails3r2e13.box

  10. Restart the machine and continue work: vagrant up

Install the base machine on student computer

OS X/Linux commands are given here, change the direction of the / (to \) for Windows.

  1. Install Virtualbox
  2. Install Vagrant
  3. Create a vms directory for your development use mkdir vms
  4. Create a machine1 directory mkdir vms/machine1 It will be accessible to both Linux and your operating system.
  5. cd vms/machine1
  6. vagrant init rails3r2e13.box url_to_image
  7. uncomment config.vm.network :private_network, ip: "192.168.33.10" in Vagrantfile
  8. vagrant up # first time will download the vm machine
  9. vagrant ssh # if this fails, you need to put SSH on the path

SSH for Windows

Windows does not include SSH by default. Two options are to 1) install Git locally and then use it’s SSH implementation and 2) install PuTTY.

Limitations

The file .bashrc has a line at the end of it that maps the rails commands onto the path along with library linkage as well. This messes up some local commands nano and others. One solution is to comment out the line and re-log into the VM.

Other vagrant commands

  • vagrant destroy eliminate the virtual machine so next time it will be rebuilt
  • vagrant reload combination of of halt then up
  • vagrant suspend pause machine copying state to disk, resume with `vagrant resule“
Vagrantfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  # Every Vagrant virtual environment requires a box to build off of.
  config.vm.box = "precise32"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  config.vm.network :private_network, ip: "192.168.33.10"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  config.vm.provision :shell, :path => "script.sh"

end
script.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env bash

set -e      # Exit script on error
set -x      # Print commands and arguments

echo "Starting configuration script"

# Abort provisioning if rubystack is already installed.
test -d /opt/rubystack-1.9.3-10 &&
{ echo "rubystack already installed."; exit 0; }

echo "Provisioning rubystack from bitnami"

# Install bitnami
/vagrant/bitnami-rubystack-1.9.3-10-linux-installer.run --mode unattended \
                                  --disable-components varnish,phpmyadmin,rvm

# install the quiet_assets gem
gem install quiet_assets -f --install-dir /opt/rubystack-1.9.3-10/ruby/lib/ruby/gems/1.9.1

# make vagrant user owner of the install
chown -R vagrant.vagrant /opt/rubystack-1.9.3-10

# install a symbolic link to rubystack directory
ln -s /opt/rubystack-1.9.3-10 /home/vagrant/rubystack

# put ruby / rails code on path
echo ". /opt/rubystack-1.9.3-10/scripts/setenv.sh" >> /home/vagrant/.bashrc

echo "Provisioning complete."

Adding to a Non-privileged Users Path in Windows 7

| Comments

Does it have to be this hard?

Anyway, if you are looking for how to add to a non-privileged user’s search path for command line usage, the command setx is what you are looking for. While it can be used to modify the system search path, users don’t have permission to do that in many enterprise deployments. The paths specified by the user are at the end of the system paths.

To see the existing path

    echo %path%

    C:\Ruby193\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\

To put C:\Users\dgreen\bin on the path, use

    setx path C:\Users\dgreen\bin

Once you start a new CMD session, you can see the impact of your changes.

    echo %path%

    C:\Ruby193\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;c:\Users\dgreen\bin