再生時間の計算

WAVEとMIDIの再生ができるようになり、再生時間を表示したいと思い調べてみた。MIDIの再生では最初から再生時間を返すメソッドが準備されているが、WAVEの再生では再生時間の計算が必要だった。

MidiPlayer.java
import java.io.*;
import javax.sound.midi.*;

class MidiPlayer {
  public static void main(String[] args) {
    MidiPlayer mp = new MidiPlayer(args[0]);
  }
  MidiPlayer(String filename) {
    try {
      Sequencer sqr = MidiSystem.getSequencer();
      sqr.setLoopCount(0);
      sqr.open();
      FileInputStream fis = new FileInputStream(filename);
      Sequence sq = MidiSystem.getSequence(fis);
      fis.close();
      sqr.setSequence(sq);
      double sec = sqr.getMicrosecondLength() / 1000 / 1000;
      System.out.println("再生時間;" + String.format("%.1f", sec) + "秒");

      sqr.start();
      while(sqr.isRunning()) {
        Thread.sleep(1000);
      }
      sqr.stop();
    } catch(Exception e) {
      e.printStackTrace();
    }
    System.exit(0);
  }
}

SequencerのgetMicrosecondLength()を使ってlong型のマイクロ秒を取得し、2回1000で割って秒単位にした。long型は整数なので、割り算の結果が少数になる可能性が高いため、double型に結果を収納。String.format()を使って少数第1位まで表示させた。

SoundPlayer.java
import javax.sound.sampled.*;
import java.io.*;

public class SoundPlayer {
  public static void main(String[] args) {
    SoundPlayer sp = new SoundPlayer(args[0]);
  }
  SoundPlayer(String filename) {
    try {
      File file = new File(filename);
      if(file.exists()) {
        AudioInputStream stream = AudioSystem.getAudioInputStream(file);
        byte[] buf = new byte[stream.available()];
        stream.read(buf, 0, buf.length);
        AudioFormat format = stream.getFormat();
        long nBytesRead = format.getFrameSize()*stream.getFrameLength();
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
        SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
        double sec = stream.getFrameLength() / format.getFrameRate();
        System.out.println("再生時間:" + String.format("%.1f", sec) + "秒");

        line.open(format);
        line.start();
        line.write(buf, 0, (int)nBytesRead);
        line.drain();
        line.close();
      } else {
        System.out.println("ファイルが見つかりませんでした。");
      }
    } catch(Exception e) {
      e.printStackTrace();
    }
    System.exit(0);
  }
}

AudioInputStreamのgetFrameLength()でlong型のサンプルフレーム数で表されるストリームの長さを取得。AudioFormatのgetFrameRate()でfloat型の1 秒当たりのフレーム数を取得。これらから秒数を算出し、先ほどと同じく小数第1位までを表示させた。

今回もGUIでMIDI再生

これまでと同様に、GUIでMIDI再生を行えるようにした。

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.sound.midi.*;

class MidiPlayer2 {
  static JFrame frame;
  static JButton playButton, stopButton;
  static JLabel counterLabel;
  static JProgressBar pBar;
  static int maxLength;
  static Sequencer sqr;

  public static void main(String[] args) {
    File file = new File("tamji02.mid");
    maxLength = -1;
    if(file.exists()) {
      try {
        sqr = MidiSystem.getSequencer();
        sqr.setLoopCount(0);
        sqr.open();
        FileInputStream fis = new FileInputStream(file.getName());
        Sequence sq = MidiSystem.getSequence(fis);
        fis.close();
        sqr.setSequence(sq);
        maxLength = (int)sqr.getMicrosecondLength();
        MidiPlayer2 mp2 = new MidiPlayer2();
        frame.setVisible(true);
        while(true) {
          int nowPosition = (int)sqr.getMicrosecondPosition();
          counterLabel.setText(nowPosition + "/" + maxLength);
          pBar.setValue(nowPosition);
          Thread.sleep(500);
          playButton.setEnabled(!sqr.isRunning());
          stopButton.setEnabled(sqr.isRunning());
          if(maxLength == nowPosition) {
            sqr.setMicrosecondPosition(0);
          }
        }
      } catch(Exception e) {
        e.printStackTrace();
      }
    } else {
      System.out.println("ファイルが見つかりませんでした。");
      System.exit(1);
    }
  }

  MidiPlayer2() {
    frame = new JFrame("ClipPlayer2");
    playButton = new JButton("再生");
    stopButton = new JButton("停止");
    stopButton.setEnabled(false);
    ActionListener al = new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        if(ae.getSource() == playButton) {
          playButton.setEnabled(false);
          stopButton.setEnabled(true);
          sqr.start();
        } else {
          playButton.setEnabled(true);
          stopButton.setEnabled(false);
          sqr.stop();
        }
      }
    };
    playButton.addActionListener(al);
    stopButton.addActionListener(al);
    counterLabel = new JLabel("0", JLabel.RIGHT);
    pBar = new JProgressBar(0, maxLength);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.add(playButton);
    frame.add(stopButton);
    frame.add(counterLabel);
    frame.add(pBar);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(350,100);
    frame.setLocationRelativeTo(null);
  }

}

これを「MidiPlayer2.java」として保存し、コンパイルすればOK。前回拾ったMIDIファイルを同じディレクトリに置いて実行すれば再生・一時停止が可能。

GUIを付けたことで気になったことがある。再生までに時間が少しかかったのは、そもそも最初の方に音が存在していなからかもしれない。でも、もしかしたら、このPCでは処理が遅く、Progressバーの動きが遅れているだけなのかもしれない。

JavaでMIDIを再生

JavaでMIDIファイルを再生できることを知り早速試した。

参考にしたのは「6 Midiファイルを再生する」。このページを元にMIDIファイルの再生をしてみる。

import java.io.*;
import javax.sound.midi.*;

class MidiPlayer {
  public static void main(String[] args) {
    MidiPlayer mp = new MidiPlayer(args[0]);
  }
  MidiPlayer(String filename) {
    try {
      Sequencer sqr = MidiSystem.getSequencer();
      sqr.setLoopCount(0);
      sqr.open();
      FileInputStream fis = new FileInputStream(filename);
      Sequence sq = MidiSystem.getSequence(fis);
      fis.close();
      sqr.setSequence(sq);
      sqr.start();
      while(sqr.isRunning()) {
        Thread.sleep(1000);
      }
      sqr.stop();
    } catch(Exception e) {
      e.printStackTrace();
    }
    System.exit(0);
  }
}

コンパイル後、「TAM Music Factory」の「音楽素材(MIDI素材)>ジングル」から「tamji02.mid」を入手した。これを同じディレクトリに置いた状態で、以下のように実行すればOK。

java MidiPlayer tamji02.mid

再生するまでに少し時間がかかるのは、このPCの性能のせいか…。

なんとかJARファイル内の音源再生に成功

Java Clipをあきらめ、最初に扱ったSourceDataLineを使う方法でいろいろと挑戦した。もともと知識が足りないこともあり、なかなかうまくClipの時と同じような動作をさせることができないまま何日も経った。

一番困ったのは、一度再生を終えると最初に戻すことができない、ということだった。

あちこち見てまわっているうちに、「とりあえずプログラミング」の「JavaでWAVファイルを再生する(2)」という記事を発見。

とすると、どうすればいいかというと、再生位置を先頭に戻すには、一度close()してから再度open()するという処理しかなさそうです。さらに任意の位置に移動させるには、skip()を使うという方法しかないようです。

ということで、skip()を使おうとあれこれ思案したが、これまたなかなかうまくいかない。

次に、「akJとackeyシリーズ 開発日記」の「JavaでWavの部分再生簡単だなー。」という記事を発見。…何をどのように処理しているのかさっぱりわからない。
(T-T)

簡単と書いてあるのにまったくわからない自分の知識と経験のなさを痛感しつつも、もっと簡単にできるのでは、という思いだけは強く、結局まったく同じものを作ろうとすることなく悩み続けることとなった。

Threadの外部からの操作やフラグでの操作など、いろいろと試してみたところ、ふと気づくと、いつの間にかなんとなくでき上がっていた。偶然の産物なので、もっとすっきりさせることができるかもしれないが…。

AudioPlayer.java
import java.io.*;
import javax.sound.sampled.*;

class AudioPlayer {

  AudioInputStream stream;
  AudioFormat format;
  DataLine.Info info;
  SourceDataLine line;
  byte[] buf;
  long nBytesRead;
  boolean playFlag, pauseFlag;
  Thread playThread;
  String filename;

  private AudioPlayer() {
  }

  AudioPlayer(String fn) {
    load(fn);
  }

  public void load(String fn) {
    filename = fn;
    load();
  }

  public void load() {
    playFlag = false;
    pauseFlag = false;
    try {
     stream = AudioSystem.getAudioInputStream(getClass().getResource(filename));
      format = stream.getFormat();
      info = new DataLine.Info(SourceDataLine.class, format);
      line = (SourceDataLine)AudioSystem.getLine(info);
      line.open(format);
      //buf = new byte[line.getBufferSize()]; //大きい数値が入ると停止までに時間がかかる
      buf = new byte[128];
    } catch(Exception e) {
      e.printStackTrace();
    }
  }

  private void makePlayThread() {
    playThread = new Thread() {
      public void run() {
        try {
          int readBytes = 0;
          while(playFlag && readBytes != -1) {
            if(pauseFlag) {
              Thread.sleep(100);
            } else {
              readBytes = stream.read(buf, 0, buf.length);
              if(readBytes >= 0) {
                line.write(buf, 0, readBytes);
              }
            }
          }
          load();
          //line.drain();
        } catch(Exception e) {
          e.printStackTrace();
        }
      }
    };
  }

  public void play() {
    playFlag = true;
    resetFlag = false;
    if(line != null) {
      if(pauseFlag) {
        pauseFlag = false;
      } else {
        makePlayThread();
        line.start();
        playThread.start();
      }
    } else {
      System.out.println("再生するデータがありません。");
    }
  }

  public void pause() {
    pauseFlag = true;
    if(line.getLongFramePosition() == stream.getFrameLength()) {
      try {
        line.close();
        stream.close();
      } catch(Exception e) {
        e.printStackTrace();
      }
      load();
    }
  }

  public long getLength() {
    if(line != null) {
      return stream.getFrameLength();
    } else {
      return 0;
    }
  }

  public long getPosition() {
    if(line != null) {
      return line.getLongFramePosition();
    } else {
      return 0;
    }
  }

}


AudioPlayerTest.java
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class AudioPlayerTest {

  JButton playButton, stopButton;
  JLabel counterLabel;
  JProgressBar pBar;
  AudioPlayer ap;

  public static void main(String[] args) {
    AudioPlayerTest apt = new AudioPlayerTest();
  }

  AudioPlayerTest() {
    ap = new AudioPlayer("sa00101.wav");
    playButton = new JButton("再生");
    stopButton = new JButton("停止");
    stopButton.setEnabled(false);
    ActionListener al = new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        playButton.setEnabled(!(playButton.isEnabled()));
        stopButton.setEnabled(!(stopButton.isEnabled()));
        if(ae.getSource() == playButton) {
          ap.play();
        } else {
          ap.pause();
        }
      }
    };
    playButton.addActionListener(al);
    stopButton.addActionListener(al);
    counterLabel = new JLabel("0", JLabel.RIGHT);
    pBar = new JProgressBar(0, (int)ap.getLength());
    JFrame frame = new JFrame(this.getClass().getSimpleName());
    frame.getContentPane().setLayout(new FlowLayout());
    frame.add(playButton);
    frame.add(stopButton);
    frame.add(counterLabel);
    frame.add(pBar);
    frame.setSize(350, 100);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    long frameLength = ap.getLength();
    while(true) {
      if(ap.getPosition() == 0) {
        playButton.setEnabled(true);
        stopButton.setEnabled(false);
      }
      counterLabel.setText(ap.getPosition() + "/" + frameLength);
      pBar.setValue((int)ap.getPosition());
      try {
        Thread.sleep(200);
      } catch(Exception e ) {
        e.printStackTrace();
      }
    }
  }

}

これらのファイルの場所に音源ファイル「sa00101.wav」を置いて以下を実行。

javac *.java
java AudioPlayerTest

無事音がなったので、「mf.txt」を用意。

mf.txt
Main-Class: AudioPlayerTest

そして、以下を実行。

jar cvfm AudioPlayerTest.jar mf.txt *.class
jar uvf AudioPlayerTest.jar sa00101.wav
java -jar AudioPlayerTest.jar

どうやらstop()を使って再生中の音を止めると、その後うまくいかない。さらに、「byte[] buf」の配列を大きくすると、停止ボタンを押してもその配列に収まったデータの再生が完了するまで音は止まらない。そのため小さな値を入れてみたが、今後これが何かしら問題になるかもしれない。

しかし、とりあえず、なんとか自分の思い通りに動くところまできた。今後は音のなるものをいろいろと作ってみたい。…時間が作れれば。

Linux + Java Clipの限界?

これまでになんとかClipを使って音を出すことができるようになったので、いよいよ汎用性を高めようと、音の操作を別のクラスにまとめてみた。

ClipPlayer.java
import java.io.*;
import javax.sound.sampled.*;

class ClipPlayer {

   private Clip clip;

  public ClipPlayer(String filename) {
    try {
      AudioInputStream stream = AudioSystem.getAudioInputStream(getClass().getResource(filename));
      AudioFormat format = stream.getFormat();
      DataLine.Info info = new DataLine.Info(Clip.class, format);
      clip = (Clip)AudioSystem.getLine(info);
      clip.open(stream);
      stream.close();
    } catch(Exception e) {
      e.printStackTrace();
    }
  }

  public void play() {
    if(clip != null) {
      if(getPosition() % (getLength() - 1) == 0) {
        clip.stop();
        clip.setFramePosition(0);
      }
      new Thread() {
        public void run() {
          clip.start();
        }
      }.start();
    } else {
      System.out.println("再生できるデータがありません。");
    }
  }

  public void pause() {
    if(clip != null) {
      clip.stop();
    }
  }

  public int getLength() {
    if(clip != null) {
      return clip.getFrameLength();
    } else {
      return 0;
    }
  }

  public long getPosition() {
    if(clip != null) {
      return clip.getLongFramePosition();
    } else {
      return 0;
    }
  }

}


ClipPlayerTest.java
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class ClipPlayerTest {

  static JFrame frame;
  static JButton playButton, stopButton;
  static JLabel counterLabel;
  static JProgressBar pBar;
  static ClipPlayer cp;

  public static void main(String[] args) {
    cp = new ClipPlayer("sa00101.wav");
    ClipPlayerTest cpt = new ClipPlayerTest();
    frame.setVisible(true);
    int positionInt = 0;
    int maxLength = cp.getLength() - 1;
    while(true) {
      positionInt = (int)cp.getPosition();
      counterLabel.setText(positionInt + "/" + maxLength);
      pBar.setValue((int)positionInt % (maxLength));
      if(positionInt % (cp.getLength() - 1) == 0) {
        playButton.setEnabled(true);
        stopButton.setEnabled(false);
      }
      try {
        Thread.sleep(500);
      } catch(Exception e) {
        e.printStackTrace();
      }
    }
  }

  ClipPlayerTest() {
    frame = new JFrame(getClass().getSimpleName());
    playButton = new JButton("再生");
    stopButton = new JButton("停止");
    stopButton.setEnabled(false);
    ActionListener al = new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        playButton.setEnabled(!playButton.isEnabled());
        stopButton.setEnabled(!stopButton.isEnabled());
        if(ae.getSource() == playButton) {
          cp.play();
        } else {
          cp.pause();
        }
      }
    };
    playButton.addActionListener(al);
    stopButton.addActionListener(al);
    counterLabel = new JLabel("0", JLabel.RIGHT);
    pBar = new JProgressBar(0, cp.getLength());
    frame.getContentPane().setLayout(new FlowLayout());
    frame.add(playButton);
    frame.add(stopButton);
    frame.add(counterLabel);
    frame.add(pBar);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(350,100);
    frame.setLocationRelativeTo(null);
  }
}

そして、前回の音源ファイル「sa00101.wav」も同じ場所に用意して、次のように実行。

javac *.java
java ClipPlayerTest

無事成功!

実は、これらの実行ファイルと音源ファイルをJARで一つにまとめて扱うことが目標。そこで、これらのファイルのある場所に「mf.txt」というテキストファイルを用意。

mf.txt
Main-Class: ClipPlayerTest

そして、さらに次のように実行。

jar cvfm ClipPlayerTest.jar mf.txt *.class
jar uvf ClipPlayerTest.jar sa00101.wav
java -jar ClipPlayerTest.jar

…あれ?最初の少しだけ再生され、あとは動いているのに音が止まってしまう。

前回の一つのclass内で再生させたものを同じようにJARでまとめると、なんの問題もなく再生できるのに、汎用性を高めようとClipの操作を別ファイルで行うと、JARでまとめた時には音がならなくなる。

色々調べたがさっぱりわからない。もしかしてうまく読み込めていなくて、「clip.open(stream)」の部分を書き換えたら動くのか?と思い、「clip.open(stream)」を消して次のように書いてみた。

byte[] buf = new byte[stream.available()];
stream.read(buf);
clip.open(format, buf, 0, buf.length);

JARにまとめなければ変更前と同様に音はなるが、JARにまとめるとまたすぐに音が止まってしまった。

Clipは一時停止や繰り返しの機能があって便利そうだったが、よくよく考えれば大きなファイルでは困るようなことが書かれているところもあったし、仕方がないのでもう一方で試してみることにする。

探してはみたものの、Clipで扱えるファイルの大きさの限界がどのくらいなのかはよくわからないまま。わかっているのは、外部のclassでClipを操作する方法ではJARにまとめたときに音がならなくなる、という現状。これもLinux + Javaのせい?

JavaのClipで音再生

前回無事Clipを使った再生ができた。そこで、GUIを使ってコントロールしようと試みたが、これがまたなかなかうまくいかない。

再生ボタンで再生し、停止ボタンをクリックすると一時的に止まり、再生ボタンで続きから再生する。ここまではうまくいったが、一度最後まで再生をすると、その後は再生できない。

いろいろ調べると、「ひでっぷの技術メモ」の「Clipで再生した音声ファイルが途中で途切れる」という記事で、Clipでは再生位置を覚えていて、再生終了時に再生位置を0に戻さなければならないことがわかった。

ところが、この記事のように組んでも動かない。これもLinuxのせい?

とりあえず、LineListenerを追加してupdate()内ではsetFramePosition(0)が実行されないことがわかった。

いろいろと考えて、ボタンをクリックしたタイミングで再生位置を確認し、終了位置ならば再生位置を0に戻すようにした。

また、実際にClipを再生してみると、getLongFramePosition()で得られる位置情報は自分の当初の考えとは異なっていた。

getLongFramePosition()で得られる位置は、1回目が終わり2回目の再生に入ると、1回目の終了位置からさらに数値が増えていく。たとえsetFramePosition(0)を行なっても、getLongFramePosition()で得られる情報は0には戻らないことがわかった。どこが再生されているかわかるように、JLabelとJProgressBarを使って再生位置を表示させてみた。

また、「音の再生」と「再生位置情報の表示」を行うためには、どうしてもThreadを使う必要があった。最初は「再生位置情報の表示」の方をThreadにして実行を試みたがうまく動かなかった。そのため、「音の再生」をThreadで行うとうまくいった。「音の再生」をThreadで行う方法は、「駆け出しプログラマの備忘録」の「JavaでWAVを再生するプログラム」に書かれていた。

ということで、次のようにつくったらうまく動いてくれた。

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.sound.sampled.*;

class ClipPlayer2 {
  static Clip clip;
  static JFrame frame;
  static JButton playButton, stopButton;
  static JLabel counterLabel;
  static JProgressBar pBar;
  static int playCount, maxLength;

  public static void main(String[] args) {
    File file = new File("sa00101.wav");
    if(file.exists()) {
      try {
        AudioInputStream stream = AudioSystem.getAudioInputStream(file);
        AudioFormat format = stream.getFormat();
        DataLine.Info info = new DataLine.Info(Clip.class, format);
        clip = (Clip)AudioSystem.getLine(info);
        clip.open(stream);
        maxLength = clip.getFrameLength() - 1;
        clip.addLineListener(new LineListener() {
          public void update(LineEvent le) {
            if(le.getType() == LineEvent.Type.STOP) {
              if(maxLength * playCount == clip.getLongFramePosition()) {
                playButton.setEnabled(true);
                stopButton.setEnabled(false);
              }
            }
          }
        });
        stream.close();
        ClipPlayer2 cp2 = new ClipPlayer2();
        frame.setVisible(true);
        while(true) {
          counterLabel.setText(clip.getLongFramePosition() + "/" + maxLength);
          pBar.setValue((int)clip.getLongFramePosition() % (maxLength + 1));
          try {
            Thread.sleep(500);
          } catch(Exception e) {
            e.printStackTrace();
          }
        }
      } catch(Exception e) {
        e.printStackTrace();
      }
    } else {
      System.out.println("ファイルが見つかりませんでした。");
      System.exit(1);
    }
  }

  ClipPlayer2() {
    frame = new JFrame("ClipPlayer2");
    playCount = 1;
    playButton = new JButton("再生");
    stopButton = new JButton("停止");
    stopButton.setEnabled(false);
    ActionListener al = new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        if(ae.getSource() == playButton) {
          playButton.setEnabled(false);
          stopButton.setEnabled(true);
          if((long)(clip.getFrameLength() - 1) * playCount == clip.getLongFramePosition()) {
            clip.stop();
            clip.setFramePosition(0);
            playCount++;
          }
          play();
        } else {
          playButton.setEnabled(true);
          stopButton.setEnabled(false);
          clip.stop();
        }
      }
    };
    playButton.addActionListener(al);
    stopButton.addActionListener(al);
    counterLabel = new JLabel("0", JLabel.RIGHT);
    pBar = new JProgressBar(0, maxLength);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.add(playButton);
    frame.add(stopButton);
    frame.add(counterLabel);
    frame.add(pBar);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(350,100);
    frame.setLocationRelativeTo(null);
  }

  public void play() {
    new Thread() {
      public void run() {
        clip.start();
      }
    }.start();
  }

}

ClipPlayer2


「otosozai.com」から音声データ「sa00101.wav」をいただいて実行してみた。再生・停止が無事操作できた。

Linux + Java で音をならす

久しぶりにJavaを勉強しようと思い、Lubuntu13.04にopenjdk-7-jdkを入れたまま放置していたので、JDK8を入れ直すべく「kiy271の日記 インフラエンジニアの備忘録」の「Lubuntu 14.04でOracle JDKのインストール」を参考に操作した。

…という話を書いて、一部を変更しようと思ったら、間違えてきれいさっぱり記事が消えてしまった!思い出しながら、あちこち探しながら書き直すことにする。

どうやら13.04では対応していないようでJDK8はインストールできなかったので、まずはOSのアップグレードを…と思ったが、13.10へのオンラインアップグレードも終了していた。仕方がないので、とりあえずOSはそのまま、openjdk-7-jdkもそのままでお勉強することにする。

なお、新たに用意したLubuntu14.04では、上記のサイトで見た方法でJDK8をインストールすることができた。

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer


openjdk-7-jdkでも、基本的なプログラムはコンパイル・実行共に問題なかった。しかし、いざ音をならそうとすると「LineUnavailableException」などのエラーが出てうまくいかなかった。

「メモ用紙の裏」の「JavaSound on Linux」という記事を見ていると、どうやらLinuxでは音の再生にアプリケーションを使っているかもしれない、と思い、「aossというコマンドを使うらしい」と書かれているので、LXTerminalを起動して「aoss」と実行してみた。すると、「alsa-oss」をインストールしろと表示された。記事にはPulseAudioについても書かれているので、ついでにこれもインストールすることにした。

sudo apt-get install alsa-oss pulseaudio


さらに、この記事に書かれているままに以下をコピペしてホームディレクトリに.asoundrcとして保存した。

pcm.!default {
 type plug
 slave.pcm "dmixer"
}
#pcm.!plughw {
#  type plug
#  slave.pcm "dmixer"
#}
#pcm.dsp {
#  type plug
#  slave.pcm "dmixer"
#}
#pcm.dsp0 {
# type plug
# slave.pcm "dmixer"
#}
ctl.mixer0 {
 type hw
 card 0
}
pcm.dmixer {
 type dmix
 ipc_key 1024
 slave {
   pcm "hw:0,0"
   period_time 0
   buffer_time 0
   period_size 1024
   buffer_size 8192
   format S32_LE
   channels 2
   rate 44100
 }
 bindings {
   0 0
   1 1
 }
}
ctl.dmixer {
 type hw
 card 0
}


これで、「失言の多いプログラマ」の「JavaでWAVファイルを読む」を参考に以下のようにプログラムを書いてみた。

import javax.sound.sampled.*;
import java.io.*;

public class SoundPlayer {
 public static void main(String[] args) {
   try {
     File file = new File("alarm.wav");
     if(file.exists()) {
       AudioInputStream stream = AudioSystem.getAudioInputStream(file);
       byte[] buf = new byte[stream.available()];
       stream.read(buf, 0, buf.length);
       AudioFormat format = stream.getFormat();
       long nBytesRead = format.getFrameSize()*stream.getFrameLength();
       DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
       SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
       line.open(format);
       line.start();
       line.write(buf, 0, (int)nBytesRead);
       line.drain();
       line.close();
     } else {
       System.out.println("ファイルが見つかりませんでした。");
     }
   } catch(Exception e) {
     e.printStackTrace();
   }
   System.exit(0);
 }
}


「SoundPlayer.java」として保存し、「javac SoundPlayer.java」とコンパイルして、同じディレクトリに音声ファイル「alarm.wav」を置いて、「java SoundPlayer」として実行すると、無事音がなった。

なぜ「alsa-oss」と「pulseaudio」を入れるだけで音がなるのだろう?いろいろ疑問が残るが、とりあえず音がなったので今回はOKとする。

ちなみに、Lubuntu14.04 + JDK8では、「alsa-oss」と「pulseaudio」を入れなくても音が再生できた。本当になぜ?まだまだ勉強が必要なようだ。

※後日追記
pulseaudioは必須なようだ。OSインストール時に「インストール中にアップデートをダウンロードする」と「サードパーティーのソフトウェアをインストールする」にチェックをつけると自動的に入るのかも…とも思ったが、チェックを付けてもダメなことも判明。おそらく何かべつのアプリケーションソフトウェアをインストールした際に依存で入ったのだろう…。


ここまでが、間違って消してしまったところ。

その後、さらにJavaで音を再生する方法を調べて回った。

Javaでの音の再生には、「TOYATAKU Web」の「音声管理」という記事によると、音を読み込みながら再生する「SourceDataLine」を使う方法と、短い音を事前にメモリ上に読み込んでおいて再生する「Clip」を使う方法の2種類があるらしい。

前回は「SourceDataLine」を使ったので、今回は「Clip」を使ってみることにする。

前回再生実験に用いた「alarm.wav」は、再生さえできればよいと考えてネット上で拾った時計の効果音でありとても短いため、実際の再生には「Clip」の方が向いているようだ。

「Clip」を利用した例はたくさんあったが、なぜかどれもエラーは出ないのに音が再生できなかった。いろいろと調べた結果、「人工知能に関する断創録」の「ClipでWAVE再生」の記事の上部から動作確認用のJARファイル「wave_engine.jar」をゲットすると、1回目だけは再生できた。一時停止したり、音が止まったあとはなぜか操作できなくなるのが気になったが…とにかく、そのJARファイルの中の「WaveEngine.java」と前回書いた「SoundPlayer.java」の2つを元に、以下のように書いたらやっと再生することができた。

import java.io.*;
import javax.sound.sampled.*;

class ClipPlayer {
 public static void main(String[] args) {
   try {
     File file = new File("alarm.wav");
     if(file.exists()) {
       AudioInputStream stream = AudioSystem.getAudioInputStream(file);
       AudioFormat format = stream.getFormat();
       DataLine.Info info = new DataLine.Info(Clip.class, format);
       Clip clip = (Clip)AudioSystem.getLine(info);
       clip.open(stream);
       clip.start();
       clip.drain();
       clip.stop();
       clip.close();
       stream.close();
     } else {
       System.out.println("ファイルが見つかりませんでした。");
     }
   } catch(Exception e) {
     e.printStackTrace();
   }
   System.exit(0);
 }
}


これを「ClipPlayer.java」として保存し、「javac ClipPlayer.java」でコンパイル、「alarm.wav」を同じディレクトリに置いておいて「java ClipPlayer」として実行すれば、無事音がなった。

なぜいろいろ公開している他の例では再生できないのだろう?わからないこと、勉強することはまだまだたっぷりあって、本当にキリがない。

古いPC(PentiumM/PAE非対応)にLubuntu14.04をインストール

最近複数のPCでWindowsXPの動きが悪くなってきた。PAE非対応のCPUではあるが、インストール前に「forcepae」を指定して、Lubuntu14.04をインストールして使うようになってきた。

詳しいインストール方法は「軽量Linuxを試す/Lubuntuのインストール」や「PCメモ lubuntu 14.04 LTSをPentium Mマシンで動かす」に書かれているが、大雑把に書いておく。

1.公式サイトから lubuntu-14.04-desktop-i386.iso を入手してCD-Rを作成。

2.作成したCD-RでPCを起動。

3.言語選択の画面でカーソル移動キーを使って「日本語」に合わせてEnterキーを押す。

4.カーソル移動キー「↓」を押して「Lubuntuをインストール」に合わせる。
 ※ Enterキーを押さない。

5.F6キー・ESCキーの順に押す(ゆっくり順番に押せばよい)。

6.「forcepae」と入力してEnterキーを押す。

7.画面に「WARNING: Forcing PAE in CPU flags」と出た後にインストーラが起動するので、そのまま指示にしたがってOSをインストールし、終了後はCD-Rを抜いて再起動。


OSインストール後の設定は、「Lubuntu 14.04 その1 - 日本語環境の構築と確認・インターネットに接続せずLubuntuをインストールした時は」に詳しく書かれている。

1.再起動後、画面左下の鳥のマークのボタン(Windowsのスタートボタンの位置)をクリック、「設定」に合わせ、「言語サポート」をクリックし、指示に従って言語サポートをインストール。

2.「日本語」が最上段にあることを確認して「システム全体に適用」をクリック。

3.「地域フォーマット」タブを選択し、「日本語」を確認して「システム全体に適用」をクリック。

4.Closeボタンをクリック後、再起動 or 再ログイン。


次に、日本語入力環境を整える。「Ubuntu・フレーバーの日本語化」に従って操作した。

1.画面左下の鳥のマークのボタン(Windowsのスタートボタンの位置)をクリックし、「アクセサリ」に合わせ、「LXTerminal」をクリック。

2.「sudo apt update」と入力し、Enterキーを押してしばらく待つ。

3.「sudo apt install fcitx-mozc」と入力し、Enterキーを押してしばらく待つ。

4.「m-config -n fcitx」と入力してEnterキーを押す。

5.一度ログアウトし、再度ログイン。


各ソフトウェアの更新を行なっておく。

1.画面左下の鳥のマークのボタン(Windowsのスタートボタンの位置)をクリックし、「システムツール」に合わせ、「ソフトウェアの更新」をクリック。

2.パスワード入力など、指示に従って操作した後待つ。

3.再起動


このあたりで概ねOSの準備完了。
あとは、好みで各種アプリケーションソフトウェアをインストール。

いつも、gimp、inkscape、audacity、wine は入れている。
wine以外なら、「LXTerminal」を起動して以下のように入力すれば、一気にインストールしてくれる。

sudo apt-get install gimp inkscape audacity


wineも次のように行えばインストールが開始されるが、途中で質問画面が表示される。

sudo apt-get install wine


どちらもTABキーを押して目的のところに色がついたらEnterキーを押せば良い。

快適。

Windows Vistaで無線LANに接続できなくなった

インターネット接続のサービス内容を変更したため、これまで使用していた機械が変わり、無線LAN親機の機能はこの新たな機械が引き継いだ。

Windows7、Windows8、Lubuntuの各PCで有線は設定不要、無線LAN接続のPCとiPad、Android携帯電話はそれぞれ無線LANの親機の登録をすませればすぐにインターネット接続はOKだった。

しかし、Windows Vistaだけはうまく動かない。なぜか「ローカルのみ」と表示され、インターネット接続ができなかった。有線にすればインターネット接続が可能だが、無線だと「ローカルのみ」なのだ。

検索してみると「Windows Vista ローカルの呪い」など、これまでにも困っていた人は多かったようだ。

あちこちに書かれていたことを次々と試してみたが、一向に改善する気配がない。

ふと、「インターネット接続サービス会社なら何か情報を持っているのでは?」と考えて、契約した会社に電話連絡をしてみた。すると、電話対応してくださった方から素晴らしい一言をいただいた。

「Windows Vistaだと、ドライバが古くて動かないことも考えられるので、パソコンメーカ側に問い合わせてドライバの更新を試していただきたいのですが…。」

ドライバの更新」か!!!!!

デバイスマネージャで見てみると、このPCの無線LANアダプタは「Atheros AR5007EG Wireless Network Adapter」とあるので、このドライバを検索すると、それと思しきサイトを発見。

Drivers for Atheros AR5007EG and Windows Vista」の「Click for Download」をクリックして早速ファイル「vista-7.7.0.498-whql.zip」をダウンロードし、ドライバをインストール後、PCを再起動した。

無事解決!Webブラウザで再びあちこちにアクセスできるようになった。

あれこれ試している途中でファイアウォールを止めていたことを思い出し、再びファイアウォールを動かしてみたが、インターネット接続についてなんら問題ない。

長い戦いがやっと終わった。

さすがWindows。いつもたくさんの学習をさせてくれる。

最新OSに飛びつかず周囲の反応を見て十分予習してから導入を決めろ、ということもこのOSが教えてくれた。

でも、正直言うと、もっと楽させてほしい。

軽量Linux

職場の古いPCを軽快に使えないか、という話になり、軽量Linuxについて調査。

以前、Ubuntuを使い始めたものの重く感じるようになったためLubuntuに変更したが、あれからしばらく経ったのでさらに軽くて使いやすいものを探してみた。

すると、「軽量Linuxを独断と偏見で調べてみたでござる」や「僕が使ってみてこれは良かったと思う軽量Linuxのすべて(追記)」というサイトでたくさんのLinux情報をゲット。

これらによると、linuxBeanPuppy系がよさそう。

Puppy系は、操作方法さえ慣れればそこそこ使える。重くなること覚悟で別のアプリケーションソフトウェア入れるという手もあるが…どうしようか。

CPUがPentiumMということもあり悩んだが、今回は最初から日本語が使えてLTS(Long Term Support)であり、「forcepae」でCD-Rから起動してインストール可能となることから、結局Lubuntu14.04を採用。

THINKPAD X40に直接LUBUNTU 14.04LTSを入れてみた。」を発見できなければ、LubuntuのCD-Rが起動できないままインストールはあきらめていただろう。

今のところはさくさく動いているが、Lubuntuも少しずつ大きくなっている模様。やはり、ゆくゆくはPuppy系…かな?


calendar

S M T W T F S
    123
45678910
11121314151617
18192021222324
252627282930 
<< September 2016 >>

selected entries

categories

archives

links

profile

書いた記事数:84 最後に更新した日:2016/09/11

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM