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

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

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

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

4回答

827閲覧

java sount api で波形を描画しようとしたのですがrepaintされません。

Lapis_nul

総合スコア6

Java

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2020/03/08 14:22

編集2020/03/15 15:10

前提・実現したいこと

java sound apiを使用して波形を描画するプログラムを作っています。

java

1import java.awt.*; 2import java.awt.event.*; 3import java.util.*; 4 5import javax.sound.sampled.*; 6import javax.swing.*; 7class recordsys extends Observable{ 8 byte wavedata[]; 9 AudioFormat format; 10 DataLine.Info info; 11 TargetDataLine target; 12 AudioInputStream input; 13 boolean recmode; 14 double width=1;//録音時間(1秒) 15 int rate=44100;//サンプリングレート 16 public recordsys() { 17 try { 18 wavedata=new byte[(int)(width*rate)]; 19 format= new AudioFormat(rate,8,1,true,false); 20 info=new DataLine.Info(TargetDataLine.class, format); 21 target=(TargetDataLine)AudioSystem.getLine(info); 22 input= new AudioInputStream(target); 23 }catch(Exception e) { 24 System.out.println("ERROR"); 25 } 26 } 27 public void startrec() { 28 int i=0; 29 try{ 30 target.open(format); 31 target.start(); 32 while(i<5) { 33 input.read(wavedata,0,wavedata.length); 34 setChanged(); 35 notifyObservers(); 36 i++; 37 } 38 }catch(Exception e) { 39 System.out.println("ERROR"); 40 } 41 } 42 public void stoprec() { 43 target.stop(); 44 target.close(); 45 } 46 public byte[] getdata() { 47 return wavedata; 48 } 49 public int getdatalength() { 50 return wavedata.length; 51 } 52} 53class wavemake extends JPanel implements Observer{ 54 int x=800; 55 byte data[]; 56 int len; 57 recordsys sys; 58 public wavemake(recordsys r) { 59 sys=r; 60 len=sys.getdatalength(); 61 data=sys.getdata(); 62 r.addObserver(this); 63 } 64 public void paintComponent(Graphics g) { 65 super.paintComponent(g); 66 for(int i=0;i<x-1;i++) { 67 g.drawLine(i,-1*data[(int)(len*(double)i/x)]+127, 68 i+1,-1*data[(int)(len*(double)(i+1)/x)]+127); 69 } 70 } 71 public void update(Observable o,Object arg) { 72 data=sys.getdata(); 73 System.out.println("repaintします"); 74 repaint(); 75 try { 76 Thread.sleep(100); 77 } catch (InterruptedException e) { 78 e.printStackTrace(); 79 } 80 } 81} 82class controler extends JPanel implements ActionListener{ 83 JButton start,stop; 84 recordsys sys; 85 public controler(recordsys s) { 86 sys=s; 87 start= new JButton("開始"); stop=new JButton("停止"); 88 start.addActionListener(this); 89 stop.addActionListener(this); 90 this.setLayout(new GridLayout(1,2)); 91 this.add(start); 92 this.add(stop); 93 } 94 public void actionPerformed(ActionEvent ev) { 95 if(ev.getSource()==start) { 96 sys.startrec(); 97 }else if(ev.getSource()==stop) { 98 sys.stoprec(); 99 } 100 } 101} 102class soundwave extends JFrame{ 103 recordsys sys; 104 controler cont; 105 wavemake wave; 106 public static void main(String[] args) { 107 new soundwave(); 108 } 109 public soundwave() { 110 sys=new recordsys(); 111 cont=new controler(sys); 112 wave=new wavemake(sys); 113 this.setSize(800,400); 114 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 115 this.add(wave); 116 this.add(cont,BorderLayout.SOUTH); 117 this.setVisible(true); 118 } 119}

Jbuttonの「開始」ボタンを押すと5秒の間1秒毎に録音した音の波形が描画されるようにしたいと考えています。しかし波形が更新されず最後の1秒分しか描画されませんでした。updateメソッドは正常に実行されているようですが何故でしょうか。

追記

このプログラムは外部の音を録音してその波形を描画するというプログラムです。
repaintの問題は解決しましたが、別に謎のノイズが発生する問題が起きてしまいました。
正常な場合
机を叩いた時の波形です。最初の数秒間は正しい波形を描けているのですが、途中から、または停止させた時に机以外から音は出していないのに、
異常な結果
このような波形が出力されていまいました。

###改良版ソースコード

java

1import java.awt.*; 2import java.awt.event.*; 3import java.util.*; 4 5import javax.sound.sampled.*; 6import javax.swing.*; 7class recordsys extends Observable implements Runnable{ 8 byte wavedata[]; 9 AudioFormat format; 10 DataLine.Info info; 11 TargetDataLine target; 12 AudioInputStream input; 13 14 wavemake make; 15 boolean recmode; 16 double width=1;//録音時間(1秒) 17 //int rate=44100;//サンプリングレート 18 int rate=8000; 19 public recordsys() { 20 try { 21 wavedata=new byte[(int)(width*rate)]; 22 format= new AudioFormat(rate,8,1,true,false); 23 info=new DataLine.Info(TargetDataLine.class, format); 24 target=(TargetDataLine)AudioSystem.getLine(info); 25 input= new AudioInputStream(target); 26 }catch(Exception e) { 27 System.out.println("ERROR"); 28 } 29 recmode=true; 30 } 31 public void stoprec() { 32 recmode=false; 33 } 34 public void run() { 35 int i=0; 36 try{ 37 target.open(format); 38 target.start(); 39 recmode=true; 40 while(recmode&&i<10) { 41 input.read(wavedata,0,wavedata.length); 42 make.setdata(wavedata); 43 setChanged(); 44 notifyObservers(); 45 i++; 46 } 47 target.stop(); 48 target.close(); 49 }catch(Exception e) { 50 System.out.println("ERROR"); 51 } 52 } 53 public byte getdata(int i) { 54 if(i>=wavedata.length)return 0; 55 else return wavedata[i]; 56 } 57 public int getdatalength() { 58 return wavedata.length; 59 } 60 public void setwavemaker(wavemake m) { 61 make=m; 62 make.setdata(wavedata); 63 } 64} 65class wavemake extends JPanel implements Observer{ 66 int x=800; 67 int len; 68 byte data[]; 69 recordsys sys; 70 public wavemake(recordsys r) { 71 sys=r; 72 len=sys.getdatalength(); 73 74 r.addObserver(this); 75 } 76 public void paintComponent(Graphics g) { 77 super.paintComponent(g); 78 int val1,val2; 79 for(int i=0;i<len-1;i++) { 80 val1=Byte.toUnsignedInt(data[i]); 81 val2=Byte.toUnsignedInt(data[i+1]); 82 g.drawLine((int)(x*(double)i/len),val1,(int)(x*(double)(i+1)/len),val2); 83 } 84 } 85 public void update(Observable o,Object arg) { 86 repaint(); 87 } 88 public void setdata(byte d[]) {data=d;} 89} 90class controler extends JPanel implements ActionListener{ 91 JButton start,stop; 92 recordsys sys; 93 public controler(recordsys s) { 94 sys=s; 95 start= new JButton("開始"); stop=new JButton("停止"); 96 start.addActionListener(this); 97 stop.addActionListener(this); 98 this.setLayout(new GridLayout(1,2)); 99 this.add(start); 100 this.add(stop); 101 } 102 public void actionPerformed(ActionEvent ev) { 103 if(ev.getSource()==start) { 104 Thread thread=new Thread(sys); 105 thread.start(); 106 }else if(ev.getSource()==stop) { 107 sys.stoprec(); 108 } 109 } 110} 111class soundwave extends JFrame{ 112 recordsys sys; 113 controler cont; 114 wavemake wave; 115 public static void main(String[] args) { 116 new soundwave(); 117 } 118 public soundwave() { 119 sys=new recordsys(); 120 wave=new wavemake(sys); 121 sys.setwavemaker(wave); 122 cont=new controler(sys); 123 this.setSize(800,400); 124 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 125 this.add(wave); 126 this.add(cont,BorderLayout.SOUTH); 127 this.setVisible(true); 128 } 129}

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

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

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

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

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

guest

回答4

0

ベストアンサー

「開始」ボタンで actionPerformed から呼ばれる sys.startrec メソッド内で実行される AudioInputStream#read によって, 計 5 秒ブロックしているものと思います.

AudioInputStream#read

オーディオ入力ストリームから数バイトを読み込み、それをバッファ配列bに格納します。実際に読み込まれたバイト数は整数として返されます。入力データが読み込めるようになるか、ストリームの終わりが検出されるか、または例外が発生するまで、このメソッドはブロックされます。

read(byte[] b, int off, int len) のほうには明示的にブロックとは書いていませんが,

オーディオ・ストリームから指定されたデータの最大バイト数まで読み込み、読み込んだバイトを指定されたバイト配列に格納します。

とあり, 最大まで読み込むとあったり, read(byte[] b) → read(byte[] b,0,b.length) という呼び出しの連鎖になっている場合が多いため, ブロックするものと推測します. (ソースは確認していません.)

Swing はシングルスレッドですので, actionPerformed 内でブロックすれば, 再描画を含めた全体が止まります.
そして, sys.startrec が終わって actionPerformed を抜ければ動き出しますが, その時バッファ(recordsys.wavedata) には最後の 1 秒分のデータしかありません.

投稿2020/03/08 19:08

編集2020/03/08 19:27
jimbe

総合スコア12632

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

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

Lapis_nul

2020/03/09 07:05

readメソッドは(この場合)1秒経過すれば抜け出す訳ではないのですか?ブロックについて全くの無知で申し訳ありません。
jimbe

2020/03/09 09:03 編集

read は 1秒で抜けますが, while で 5 回行っています. notifyObservers → update → repaint で 1 秒毎に再描画されるとお考えでしょうが, repaint は Swing への再描画の "依頼" で, repaint を呼べば即再描画されるわけではありません. Swing は未だ開始ボタンの処理を実行中ですので, "依頼"はそれが終わらないと実行されません. "Swing がシングルスレッド" というのはそういうことです.
Lapis_nul

2020/03/09 12:26

シングルスレッドということは、何か別のスレッドを生成しその中でrepaintする手段は使えないので、その代わりにngsvxさんが仰る通りSwingUtilities.invokeLaterを使用しなければならないということですね?
jimbe

2020/03/09 12:51 編集

"Swing が" シングルスレッドで動作しているというだけで, java 的には別のスレッドは生成できます. しかし repaint のみを SwingUtilities.invokeLater で行っても無意味かと思います. SwingUtilities.invokeLater によって送られる先が, まさしく repaint の"依頼" の送り先だからです. 別スレッドにするとすれば, それは音を取り込む個所かと思います. 開始ボタンの押下によって別スレッドを起動, そのスレッド内で録音し, 1秒分のデータを得たらそのデータを wavemake に送り, 描画するというものです.
Lapis_nul

2020/03/09 14:17

その方法で考えてみたいと思います。色々と教えて頂きありがとうございました。
Lapis_nul

2020/03/10 14:15

またすいません... ```java class recordsys extends Observable implements Runnable{ byte wavedata[]; AudioFormat format; DataLine.Info info; TargetDataLine target; AudioInputStream input; boolean recmode; double width=1;//録音時間(1秒) int rate=44100;//サンプリングレート public recordsys() { try { wavedata=new byte[(int)(width*rate)]; format= new AudioFormat(rate,8,1,true,false); info=new DataLine.Info(TargetDataLine.class, format); target=(TargetDataLine)AudioSystem.getLine(info); input= new AudioInputStream(target); }catch(Exception e) { System.out.println("ERROR"); } recmode=true; } public void stoprec() { recmode=false; } public void run() { int i=0; try{ target.open(format); target.start(); recmode=true; while(recmode&&i<5) { input.read(wavedata,0,wavedata.length); setChanged(); notifyObservers(); i++; } target.stop(); target.close(); }catch(Exception e) { System.out.println("ERROR"); } } public byte getdata(int i) { if(i>=wavedata.length)return 0; else return wavedata[i]; } public int getdatalength() { return wavedata.length; } } class wavemake extends JPanel implements Observer{ int x=800; int len; recordsys sys; public wavemake(recordsys r) { sys=r; len=sys.getdatalength(); r.addObserver(this); } public void paintComponent(Graphics g) { super.paintComponent(g); for(int i=0;i<x-1;i++) { g.drawLine(i,-1*sys.getdata((int)(len*(double)i/x))+127, i+1,-1*sys.getdata((int)(len*(double)(i+1)/x))+127); } } public void update(Observable o,Object arg) { repaint(); } } class controler extends JPanel implements ActionListener{ JButton start,stop; recordsys sys; public controler(recordsys s) { sys=s; start= new JButton("開始"); stop=new JButton("停止"); start.addActionListener(this); stop.addActionListener(this); this.setLayout(new GridLayout(1,2)); this.add(start); this.add(stop); } public void actionPerformed(ActionEvent ev) { if(ev.getSource()==start) { Thread thread=new Thread(sys); thread.start(); }else if(ev.getSource()==stop) { sys.stoprec(); } } } ``` このように書き換えて1秒毎に更新するようにできたのですが、沢山のノイズが走り想定している物と全く違う波形が現れてしまいました。
jimbe

2020/03/10 16:42 編集

コメント欄で ``` によるフォーマット(マークダウン) は使えません. ご質問に追記するしかありません. 結果につきましては, どのような音がどのようなデータになるのが正解なのかの知識は私にはありませんので, 判断は出来ません. ただ一つ気になっていたのはバッファです. マルチで動作させると, wavedata への書き込みと読み出しが同時に起こります. つまり, 1 秒のデータが集まって WaveMake パネルが wavedata を読みながら線を書いている間も, recordsys は次の 1 秒分のデータを wavedata に上書きしているはずです. 読み込みのほうが早ければ逃げ切るかと思いますが, 書き込みのほうが早ければどこかで追い越し, グラフの途中から次の1秒のデータに変わってしまうかもしれません. この辺りは, 表示用と録音用でバッファを切り替える等の管理が必要かもしれません.
Lapis_nul

2020/03/17 15:11

詳しくは追記を確認して欲しいのですが、バッファを読込用と描画用で分離しても同じような症状が起きてしまいます。デバックでrecordsysクラスのwavedataやwavemakeクラスのdataの中身を確認しても-127や128といった数字は見受けられませんでした。となるとpaintcomponentに問題があるのでしょうか。
jimbe

2020/03/17 17:07 編集

> paintcomponentに問題があるのでしょうか 不確定なデータでは正解が分かり難いですので, 例えばデータとしてサイン波を入力されたかのように recordsys を弄ってみると分かるかと思います. コメントに書かれた recordsys の run メソッドを public void run() { int i=0; try{ //target.open(format); //target.start(); recmode=true; int j=0; while(recmode&&i<5) { //input.read(wavedata,0,wavedata.length); j=createSinData(wavedata,j); setChanged(); notifyObservers(); i++; } //target.stop(); //target.close(); }catch(Exception e) { e.printStackTrace(); } } とし, 直下に追加で サインデータを生成する createSinData を追加します. private int createSinData(byte[] wavedata, int j) { for(int i=0; i<wavedata.length; i++, j++) { wavedata[i] = (byte)(Math.sin(Math.toRadians(j/100))*127); } try { Thread.sleep(1000); //[ms] } catch(InterruptedException ignore) { } return j; } こちらで実行した限りでは, (「バッファを読込用と描画用で分離」前のコードですので) 「グラフの途中から次の1秒のデータに変わってしまう」現象はみられましたが, ごちゃごちゃのデータにはなりませんでした.
guest

0

setdataメソッドで、オブジェクトを代入するのではなく、cloneメソッドで配列をコピーすることによって解決出来ました。皆さま、特にjimbe様には大変お世話になりました。本当にありがとうございました。

投稿2020/03/19 15:11

Lapis_nul

総合スコア6

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

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

0

setdataメソッドについてオブジェクトを代入するのではなく、cloneメソッドで配列をコピーさせることで解決しました。皆さま、特にjimbe様には大変お世話になりました。ありがとうございました。

投稿2020/03/19 15:09

Lapis_nul

総合スコア6

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

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

0

repaint()を別スレッドで、SwingUtilities.invokeLaterを使って実行してみたらどうでしょうか。

投稿2020/03/08 15:41

ngsvx

総合スコア287

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問