実装したいもの
・再生ボタンをクリックすると直ちに、三秒間くらいかけて音量が0→目標の音量にフェードインする。三秒後はずっと目標の音量のままループ再生される。
・停止ボタンをクリックすると直ちに、三秒間くらいかけて音量が0に近づき三秒後には0になる。
実際に聞いてみると単に三秒後に音声が開始・三秒後に音声が停止しているようにしか聞こえません。
どうすればフェードイン・フェードアウトとも、徐々に音量が変化しそれが自然に聞こえるようになるでしょうか?
また、このコードで別スレッドを生成している理由は、フェードインおよびフェードアウトの処理が時間を要するため、メインスレッド(GUIスレッド)で実行するとGUIの応答が悪くなる可能性があるからです。
該当のソースコード
package sample; import java.awt.FlowLayout; import java.awt.event.ItemEvent; import java.io.IOException; import java.net.URL; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JSlider; import javax.swing.JToggleButton; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class Volume extends JFrame { private Clip clip; private FloatControl volumeControl; private JLabel volumeLabel; private Thread fadeThread; public Volume() { setTitle("サンプル"); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new FlowLayout()); // トグルボタンを作成し、初期テキストを設定 JToggleButton toggleButton = new JToggleButton("再生"); // トグルボタンの選択状態に応じてテキストを変更 toggleButton.addItemListener(e -> { if (e.getStateChange() == ItemEvent.SELECTED) { toggleButton.setText("停止"); fadeInSound(); } else { toggleButton.setText("再生"); fadeOutSound(); } }); // トグルボタンをフレームに追加 add(toggleButton); // 音声ファイルを読み込む try { URL url = getClass().getResource("kiroku.wav"); AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url); clip = AudioSystem.getClip(); clip.open(audioInputStream); volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); } catch (UnsupportedAudioFileException | IOException | LineUnavailableException ex) { System.err.println("音声ファイルの読み込みに失敗しました: " + ex.getMessage()); } // ボリュームスライダーを作成 JSlider volumeSlider = new JSlider(0, 100, 50); // 初期ボリュームを50に設定 volumeSlider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { float volume = volumeSlider.getValue() / 100f; if (volume == 0) { volumeControl.setValue(volumeControl.getMinimum()); } else { volumeControl.setValue(20f * (float) Math.log10(volume)); } volumeLabel.setText("ボリューム: " + volumeSlider.getValue()); } }); add(volumeSlider); // ボリュームラベルを作成 volumeLabel = new JLabel("ボリューム: " + volumeSlider.getValue()); add(volumeLabel); // 初期ボリュームの設定 float initialVolume = volumeSlider.getValue() / 100f; if (initialVolume == 0) { volumeControl.setValue(volumeControl.getMinimum()); } else { volumeControl.setValue(20f * (float) Math.log10(initialVolume)); } } private void fadeInSound() { if (clip != null) { if (fadeThread != null && fadeThread.isAlive()) { fadeThread.interrupt(); } fadeThread = new Thread(() -> { float targetVolume = volumeControl.getValue(); float currentVolume = volumeControl.getMinimum(); float step = (targetVolume - currentVolume) / 100f; // 3秒間で100ステップ clip.setFramePosition(0); // 再生位置を先頭に設定 clip.start(); try { for (int i = 0; i < 100; i++) { currentVolume += step; volumeControl.setValue(currentVolume); Thread.sleep(30); // 30ミリ秒待機 } volumeControl.setValue(targetVolume); // 最後に目標ボリュームに設定 } catch (InterruptedException ex) { Thread.currentThread().interrupt(); // 割り込みを再設定 } }); fadeThread.start(); } } private void fadeOutSound() { if (fadeThread != null && fadeThread.isAlive()) { fadeThread.interrupt(); } fadeThread = new Thread(() -> { float currentVolume = volumeControl.getValue(); float step = currentVolume / 100f; // 3秒間で100ステップ try { for (int i = 0; i < 100; i++) { currentVolume -= step; volumeControl.setValue(currentVolume); Thread.sleep(30); // 30ミリ秒待機 } clip.stop(); // 最後に音声を停止 } catch (InterruptedException ex) { Thread.currentThread().interrupt(); // 割り込みを再設定 } }); fadeThread.start(); } public static void main(String[] args) { Volume frame = new Volume(); frame.setVisible(true); } }
### 試したこと(バグがあるようです、ボリュームが100の時音声が流れません。)
フェードイン関数とフェードアウト関数を下記のように調整してみましたが、停止ボタンを押して0.5秒には音量をスライダーの目標音量の4分の1まで下げるという処理にしたのですが、実際に停止ボタンを押して聞いてみると0.5秒後に音量が4分の1も下がったようには聞こえません。
なぜなのか、どなたか教えて頂けると幸いです。
java
12 // 省略3 4 5 6 public Volume() {7 // 省略8 9 // ボリュームスライダーを作成(省略)10 11 12 // ボリュームラベルを作成(省略)13 14 // 初期ボリュームの設定(省略)15 16 17 private void fadeInSound() {18 if (clip != null) {19 if (fadeThread != null && fadeThread.isAlive()) {20 fadeThread.interrupt();21 }22 fadeThread = new Thread(() -> {23 float targetVolume = volumeControl.getValue();24 volumeControl.setValue(volumeControl.getMinimum()); // 音量を最小に設定25 clip.setFramePosition(0); // 再生位置を先頭に設定26 clip.start(); // 再生を開始27 float currentVolume = volumeControl.getMinimum();28 float oneThirdVolume = targetVolume / 3; // 目標音量の3分の129 30 // 0.8秒後に目標音量の3分の1まで上げる31 float step1 = (oneThirdVolume - currentVolume) / 267f; // 0.8秒間で267ステップ32 try {33 for (int i = 0; i < 267; i++) {34 currentVolume += step1;35 volumeControl.setValue(currentVolume);36 Thread.sleep(3); // 3ミリ秒待機37 }38 } catch (InterruptedException ex) {39 Thread.currentThread().interrupt(); // 割り込みを再設定40 }41 42 // 残りの2.2秒間で目標音量までフェードイン43 float step2 = (targetVolume - oneThirdVolume) / 733f; // 2.2秒間で733ステップ44 try {45 for (int i = 0; i < 733; i++) {46 currentVolume += step2;47 volumeControl.setValue(currentVolume);48 Thread.sleep(3); // 3ミリ秒待機49 }50 volumeControl.setValue(targetVolume); // 最後に目標ボリュームに設定51 } catch (InterruptedException ex) {52 Thread.currentThread().interrupt(); // 割り込みを再設定53 }54 });55 fadeThread.start();56 }57 }58 59 private void fadeOutSound() {60 if (fadeThread != null && fadeThread.isAlive()) {61 fadeThread.interrupt();62 }63 fadeThread = new Thread(() -> {64 float currentVolume = volumeControl.getValue();65 float oneQuarterVolume = currentVolume / 4; // 目標音量の4分の166 67 // 0.5秒後に目標音量の4分の1まで下げる68 float step1 = (currentVolume - oneQuarterVolume) / 167f; // 0.5秒間で167ステップ69 try {70 for (int i = 0; i < 167; i++) {71 currentVolume -= step1;72 volumeControl.setValue(currentVolume);73 Thread.sleep(3); // 3ミリ秒待機74 }75 } catch (InterruptedException ex) {76 Thread.currentThread().interrupt(); // 割り込みを再設定77 }78 79 // 残りの2.5秒間で音量を0にフェードアウト80 float step2 = oneQuarterVolume / 833f; // 2.5秒間で833ステップ81 try {82 for (int i = 0; i < 833; i++) {83 currentVolume -= step2;84 volumeControl.setValue(currentVolume);85 Thread.sleep(3); // 3ミリ秒待機86 }87 clip.stop(); // 最後に音声を停止88 } catch (InterruptedException ex) {89 Thread.currentThread().interrupt(); // 割り込みを再設定90 }91 });92 fadeThread.start();93 }94 95 public static void main(String[] args) {96 Volume frame = new Volume();97 frame.setVisible(true);98 }99}100
0 コメント