質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Eclipse

Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。

Q&A

1回答

3459閲覧

JavaFXで折れ線グラフを描画する際、二度目以降ウィンドウが立ち上がらなくなってしまいます。

kerakail

総合スコア10

JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Eclipse

Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。

0グッド

0クリップ

投稿2018/11/03 05:24

前提・実現したいこと

Javaを使用してwavファイルの波形を描画するプログラムを作成しています。
ファイルパス入力画面(図1)のテキストフィールドにファイルパスを入力して表示ボタンを押すと、
波形表示画面(図2)が立ち上がり波形が折れ線グラフで描画されるという仕組みです。
ファイルパス入力画面はSwing、波形表示画面はJavaFXを使用して作成しました。
JavaFXでの波形表示は(軽Lab_javaで音声波形データを表示してみる)を参考にさせていただきました。

イメージ説明
↑図1.ファイルパス入力画面(Swing)

イメージ説明
↑図2.波形表示画面(JavaFX)

発生している問題・エラーメッセージ

問題1
一度目は問題なく描画できるのですが、
波形表示画面を一旦閉じて別のファイルパスを入力後、
もう一度表示ボタンを押すとエラーが出てしまい二度目以降は波形表示画面が立ち上がらなくなってしまいます。
(エラー内容は下記参照)

問題2
現在は音声データ配列の要素を35個飛ばしで折れ線グラフとして描画しているのですが、
描画処理に時間がかかってしまいます。
これ以上要素を間引くと波形がスカスカになってしまうので、何か別の方法で短時間で波形を描画する方法はありませんでしょうか。

二度目以降のエラー内容 Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Application launch must not be called more than once at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:162) at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:143) at javafx.application.Application.launch(Application.java:252) at WaveDraw.lambda$0(WaveDraw.java:148) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022) at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252) at java.awt.Component.processMouseEvent(Component.java:6539) at javax.swing.JComponent.processMouseEvent(JComponent.java:3324) at java.awt.Component.processEvent(Component.java:6304) at java.awt.Container.processEvent(Container.java:2239) at java.awt.Component.dispatchEventImpl(Component.java:4889) at java.awt.Container.dispatchEventImpl(Container.java:2297) at java.awt.Component.dispatchEvent(Component.java:4711) at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4904) at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4535) at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4476) at java.awt.Container.dispatchEventImpl(Container.java:2283) at java.awt.Window.dispatchEventImpl(Window.java:2746) at java.awt.Component.dispatchEvent(Component.java:4711) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:760) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:709) at java.awt.EventQueue$3.run(EventQueue.java:703) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:84) at java.awt.EventQueue$4.run(EventQueue.java:733) at java.awt.EventQueue$4.run(EventQueue.java:731) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74) at java.awt.EventQueue.dispatchEvent(EventQueue.java:730) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

ソースコード

Java

1/* 2 * 波形描画プログラム 3 * 問題1:2度目以降の描画ができない 4 * 問題2:描画処理に時間がかかる 5 */ 6 7 8import java.awt.Color; 9import java.io.File; 10import java.nio.ByteBuffer; 11import java.nio.ByteOrder; 12 13import javax.sound.sampled.AudioFormat; 14import javax.sound.sampled.AudioInputStream; 15import javax.sound.sampled.AudioSystem; 16import javax.swing.JButton; 17import javax.swing.JFrame; 18import javax.swing.JLabel; 19import javax.swing.JOptionPane; 20import javax.swing.JTextField; 21 22import javafx.application.Application; 23import javafx.scene.Node; 24import javafx.scene.Scene; 25import javafx.scene.chart.LineChart; 26import javafx.scene.chart.NumberAxis; 27import javafx.scene.chart.XYChart; 28import javafx.scene.chart.XYChart.Series; 29import javafx.scene.layout.HBox; 30import javafx.stage.Stage; 31 32 33public class WaveDraw extends Application { 34 private static File file = null;// 波形を描画するファイル 35 private final double sec = 10;// チャートに表示する秒数 36 private final int skip = 35;// 描画時に間引く個数 37 private AudioFormat format = null; 38 private int[] values = null; 39 40 private static void messageBox(String message) { 41 // メッセージBoxを表示するメソッド 42 JFrame frameMB = new JFrame(); 43 JLabel labelMB = new JLabel(message); 44 labelMB.setForeground(Color.RED); 45 JOptionPane.showMessageDialog(frameMB,labelMB); 46 } 47 48 @Override 49 public void start(Stage stage) throws Exception { 50 // フォント色がおかしくなることへの対処 51 System.setProperty( "prism.lcdtext" , "false" ); 52 // シーングラフの作成 53 HBox box = new HBox(); 54 // チャートを作成 55 init(); 56 box.getChildren().add( createLineChart() );// 折れ線グラフの追加 57 // シーンの作成 58 Scene scene = new Scene( box , 900 , 300 ); 59 // ウィンドウ表示 60 stage.setScene( scene ); 61 stage.show(); 62 stage.setResizable(false); 63 } 64 65 public void init() throws Exception { 66 // 音声ストリームを取得 67 AudioInputStream is = AudioSystem.getAudioInputStream( file ); 68 // メタ情報の取得 69 format = is.getFormat(); 70 // 取得する標本数を計算 71 int mount = (int) ( format.getSampleRate() * sec ); 72 // 音声データの取得 73 values = new int[ mount ]; 74 for( int i=0 ; i<mount ; i++ ) { 75 // 1標本分の値を取得 76 int size = format.getFrameSize(); 77 byte[] data = new byte[ size ]; 78 int readedSize = is.read(data); 79 // データ終了でループを抜ける 80 if( readedSize == -1 ){ break; } 81 // 1標本分の値を取得 82 switch( format.getSampleSizeInBits() ) { 83 case 8: 84 values[i] = (int) data[0]; 85 break; 86 case 16: 87 values[i] = (int) ByteBuffer.wrap( data ).order( ByteOrder.LITTLE_ENDIAN ).getShort(); 88 break; 89 default: 90 } 91 } 92 // 音声ストリームを閉じる 93 is.close(); 94 } 95 96 @SuppressWarnings("unchecked") 97 public Node createLineChart() { 98 // 折れ線グラフ 99 NumberAxis xAxis = new NumberAxis(); 100 NumberAxis yAxis = new NumberAxis(); 101 LineChart<Number, Number> chart = new LineChart<Number, Number>( xAxis , yAxis ); 102 chart.setMinWidth( 900 ); 103 // データを作成 104 Series< Number , Number > series1 = new Series<Number, Number>(); 105 series1.setName( "チャンネル1" ); 106 for( int i=0 ; i<values.length / skip ; i++ ) { 107 series1.getData().add( new XYChart.Data<Number, Number>( i * skip , values[i * skip] ) ); 108 } 109 // データを登録 110 chart.getData().addAll( series1 ); 111 // タイトルを設定 112 String title = String.format( sec + "秒間の音声波形" ); 113 chart.setTitle( title ); 114 // 見た目を調整 115 chart.setCreateSymbols(false);// シンボルを消去 116 series1.getNode().lookup(".chart-series-line").setStyle("-fx-stroke-width: 1px;");// 線を細く 117 return chart; 118 } 119 120 public static void main(String[] args) { 121 JFrame frame = new JFrame("波形の表示"); 122 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 123 frame.setSize(460, 160); 124 frame.setLocationRelativeTo(null); 125 frame.setLayout(null); 126 frame.setResizable(false); 127 // ラベルの作成 128 JLabel label = new JLabel("波形を表示したい音声ファイルのファイルパスを入力してください"); 129 label.setBounds(30, 20, 390, 30); 130 // テキストフィールドの作成 131 JTextField text = new JTextField("例:C:/Music/SoundFile.wav"); 132 text.setBounds(20, 50, 300, 30); 133 // ボタンの作成 134 JButton button = new JButton("表示"); 135 button.setBounds(330, 50, 90, 30); 136 // フレームに追加&フレームを表示 137 frame.add(label); 138 frame.add(text); 139 frame.add(button); 140 frame.setVisible(true); 141 142 // ボタンアクションを追加 143 button.addActionListener(e -> { 144 file = new File( text.getText() ); 145 // ファイルの存在を確認 146 if(file.exists()) { 147 // 存在した場合は波形を描画 148 launch(args); 149 }else { 150 // 存在しない場合はメッセージを出す 151 messageBox("ファイルパスが間違っています"); 152 } 153 }); 154 } 155} 156

補足情報(FW/ツールのバージョンなど)

使用言語:Java
IDE:Eclipse

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

問題1

原因は複数回Application#launchを呼び出していることです。Application#launchは「アプリケーションの実行シナリオを開始させる」という意味合いのもので一つのプログラムで1回しか行えません。

個人的にはJavaFXアプリケーションでmainメソッドを定義しない方がよいと思います。なにがしかの初期化・終了処理が必要ならinit/stopメソッドに書けばよく、mainに書くべきことがないからです。JavaFXアプリケーションには特別な規則がありましてmainメソッドを定義しなくてもよいことになっています。書かなくてもJavaFXランタイムの方で自動的にlaunchを呼び出してくれます。

また本件に直接関係しないですがJavaFXとswingの混在は個人的にお勧めしません。「昔swingで作った画面があってどうしてもそれを流用したい」といった場合ならよいでしょうがそうでないならJavaFXだけで構成した方が安心だと思います。混在させるとJavaFXとswingの両方の知識(場合によっては深い知識)が必要になってしまうと思うのです。

最初に表示するダイアログはJavaFXでも簡単に作れますので、そうしてはいかがでしょう。

問題2

詳しくコードを見てはないので単なる思いつきですが・・・
Chartは高度なグラフを描画するのに便利ではありますが、高機能であるがゆえにあまり極端に複雑なものですと速度は望めないと思います。Canvasを用いて自前で描画する方がずっと高速だと思います。しかしCanvasを用いると軸やメモリなどChartが自動で描画してくれるところを自前で実装する必要が出てきて「メンドクサイ!」と思われるかも知れません。それはそのとおりと思いますが、10秒ぐらいの音声の表示に何秒も待たなければならないという「使いずらさ」と天秤にかけると自分なら「とりあえずしかたない」とあきらめます。

しかしそれでもなんとかしたいと考えるのがプログラマーの常だと思います。もしかしたら軸だけをChartで描画してグラフそのものはChartに重ねて配置したCanvasに直接描画するなんてアイデアが使えるかも知れません。そうした工夫も試す価値があると思います。

投稿2018/11/03 07:14

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kerakail

2018/11/05 06:30

ご回答ありがとうございます。 現在のSwingで作成したボタンを押すとJavaFXの画面を立ち上げるという仕組みのままで、 複数回Applicaionを起動させるということはできないのでしょうか。 実は上記のプログラムは質問投稿用に組んだ簡易版プログラムでして、 実際は仰る通り昔Swingで作成した音響アプリがあり、 それの拡張機能として波形表示機能を実装する際にJavaFXに手を出したのです。 やはり、Swingで作成した音響アプリを一からJavaFXで組み直すしか方法はないのでしょうか。
KSwordOfHaste

2018/11/05 11:34

なるほど事情がわかりました。 選択肢がいくつかあると思いますが、改めてちょっと伺いたいことがあります。個人用のアプリだと思ってよいでしょうか?つまり保守性やコストはとりあえずおいて技術面のみ検討するスタンスでよいでしょうか?
kerakail

2018/11/11 00:39 編集

ご返信が遅れてしまい申し訳ありません。 コメントに対する返信はメールで通知が来ないことを知らず確認が遅れてしまいました。 Javaの勉強用に個人的に作成したプログラムなので技術面のみで大丈夫です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問