jDrumスタジオリズムエミュレーター

序文:私はスタジオを備えており、スタジオでは電子ミディドラム、パッド付きの楽器(medeli、akai、novation)を購入することにしました。



Linux(Ubuntu)は開発用のコンピューターにインストールされています。上記のデバイスのソフトウェアはLinuxでサポートされておらず、ワインと仮想マシンのトラブルやオペレーティングシステムの切り替えは価値がありません。



リズムを書くためのシンプルな楽器を開発することにしました。







このリンクからプログラムダウンロードしてテストします。



設計



設計は、NetBeansでインターフェースを描画することから始まりました。



画像



動作原理



サンプルを行にロードするためのアクティブテキストフィールド。



押されたときに16個のボタン。回線にインストールされたサンプルを再現します。



[再生]ボタンは、サンプルがインストールされたスピーカーで、特定の遅延でサウンドを再生します(サンプルが回線にインストールされ、ボタンが押された場合)。



明確にコーディングする



このクラスのJDrum.javaは次の場所にあります。



  1. フレーム開始。
  2. ロジックの主要部分。
  3. 変数のセット。


Jdrum.java
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jdrum; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.util.Arrays; import java.util.List; /** * * @author dj DNkey */ public class JDrum { /** * pads values */ public static int[] pads = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * pads in line 1 */ public static Integer[] line1Pads = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; /** * pads in line 2 */ public static Integer[] line2Pads = {17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32}; /** * pads in line 3 */ public static Integer[] line3Pads = {33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48}; /** * pads in line 4 */ public static Integer[] line4Pads = {49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64}; /** * pads in line 5 */ public static Integer[] line5Pads = {65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80}; /** * pads in line 6 */ public static Integer[] line6Pads = {81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96}; /** * pads in column 1 */ public static int[] column1 = {1,17,33,49,65,81}; /** * pads in column 2 */ public static int[] column2 = {2,18,34,50,66,82}; /** * pads in column 3 */ public static int[] column3 = {3,19,35,51,67,83}; /** * pads in column 4 */ public static int[] column4 = {4,20,36,52,68,84}; /** * pads in column 5 */ public static int[] column5 = {5,21,37,53,69,85}; /** * pads in column 6 */ public static int[] column6 = {6,22,38,54,70,86}; /** * pads in column 7 */ public static int[] column7 = {7,23,39,55,71,87}; /** * pads in column 8 */ public static int[] column8 = {8,24,40,56,72,88}; /** * pads in column 9 */ public static int[] column9 = {9,25,41,57,73,89}; /** * pads in column 10 */ public static int[] column10 = {10,26,42,58,74,90}; /** * pads in column 11 */ public static int[] column11 = {11,27,43,59,75,91}; /** * pads in column 12 */ public static int[] column12 = {12,28,44,60,76,92}; /** * pads in column 13 */ public static int[] column13 = {13,29,45,61,77,93}; /** * pads in column 14 */ public static int[] column14 = {14,30,46,62,78,94}; /** * pads in column 15 */ public static int[] column15 = {15,31,47,63,79,95}; /** * pads in column 16 */ public static int[] column16 = {16,32,48,64,80,96}; /** * Sound files bind on lines 1-10 */ public static Sound line1Sound = null; public static Sound line2Sound = null; public static Sound line3Sound = null; public static Sound line4Sound = null; public static Sound line5Sound = null; public static Sound line6Sound = null; /** * play speed */ public static int speed = 35; public static boolean play = false; public static Main frame; /** * * @param args the command line arguments */ public static void main(String[] args) { new Player().start(); frame = new Main(); frame.setVisible(true); } /** * Play object Sound in new Thread * @param sound */ public static synchronized void play(Sound sound){ if(sound != null){ new PlaySound(sound).start(); } } public static synchronized void loadSound(File file){ // sound } /** * Play pressed pad * @param padNum */ public static synchronized void playPad(int padNum){ //change pads value 1 to 0, 0 to 1 if(pads[padNum - 1] == 0){ JDrum.pads[padNum - 1] = 1; } else{ JDrum.pads[padNum - 1] = 0; } /** * Check line */ if(pads[padNum - 1] == 1){ playLine(padNum); } } /** * play sound file on line where press pad * @param padNum */ public static synchronized void playLine(int padNum){ int line = getPadLine(padNum); /** * Play sound from line */ if(line == 1){ JDrum.play(line1Sound); } if(line == 2){ JDrum.play(line2Sound); } if(line == 3){ JDrum.play(line3Sound); } if(line == 4){ JDrum.play(line4Sound); } if(line == 5){ JDrum.play(line5Sound); } if(line == 6){ JDrum.play(line6Sound); } } /** * get line of pressed pad * @param padNum * @return */ public static synchronized int getPadLine(int padNum){ int line = 0; List<Integer> list; list = Arrays.asList(line1Pads); if(list.contains(padNum)){ line = 1; } list = Arrays.asList(line2Pads); if(list.contains(padNum)){ line = 2; } list = Arrays.asList(line3Pads); if(list.contains(padNum)){ line = 3; } list = Arrays.asList(line4Pads); if(list.contains(padNum)){ line = 4; } list = Arrays.asList(line5Pads); if(list.contains(padNum)){ line = 5; } list = Arrays.asList(line6Pads); if(list.contains(padNum)){ line = 6; } return line; } /** * Save JDrum project to file .drum * @param fileName */ public static void save(String fileName){ //load JDrum settings to save class Save save = new Save(); save.pads = JDrum.pads; if(line1Sound != null){ save.line1Sound = line1Sound.file.getAbsolutePath(); } if(line2Sound != null){ save.line2Sound = line2Sound.file.getAbsolutePath(); } if(line3Sound != null){ save.line3Sound = line3Sound.file.getAbsolutePath(); } if(line3Sound != null){ save.line4Sound = line4Sound.file.getAbsolutePath(); } if(line5Sound != null){ save.line5Sound = line5Sound.file.getAbsolutePath(); } if(line6Sound != null){ save.line6Sound = line6Sound.file.getAbsolutePath(); } save.save(fileName); } /** * Open saved file and load to JDrum * @param filePath */ public static void open(String filePath){ Save save = new Save(); save = save.load(filePath); Sound sound; //line1Sound = new File(save.line1Sound); if(save.line1Sound != null){ sound = new Sound(); sound.loadFile(new File(save.line1Sound)); line1Sound = sound; Main.jTextField1.setText(line1Sound.file.getName()); } if(save.line2Sound != null){ sound = new Sound(); sound.loadFile(new File(save.line2Sound)); line2Sound = sound; Main.jTextField2.setText(line2Sound.file.getName()); } if(save.line3Sound != null){ sound = new Sound(); sound.loadFile(new File(save.line3Sound)); line3Sound = sound; Main.jTextField3.setText(line3Sound.file.getName()); } if(save.line4Sound != null){ sound = new Sound(); sound.loadFile(new File(save.line4Sound)); line4Sound = sound; Main.jTextField4.setText(line4Sound.file.getName()); } if(save.line5Sound != null){ sound = new Sound(); sound.loadFile(new File(save.line5Sound)); line5Sound = sound; Main.jTextField5.setText(line5Sound.file.getName()); } if(save.line6Sound != null){ sound = new Sound(); sound.loadFile(new File(save.line6Sound)); line6Sound = sound; Main.jTextField6.setText(line6Sound.file.getName()); } JDrum.pads = save.pads; frame.changeButton(JDrum.pads); } public static void startRecording() { String command = "audio-recorder -c start"; String output = executeCommand(command); } public static void stopRecording() { String command = "audio-recorder -c stop"; String output = executeCommand(command); } public static String executeCommand(String command) { StringBuffer output = new StringBuffer(); Process p; try { p = Runtime.getRuntime().exec(command); p.waitFor(); BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); String line = ""; while ((line = reader.readLine())!= null) { output.append(line + "\n"); } } catch (Exception e) { e.printStackTrace(); } return output.toString(); } }
      
      







Player.javaデーモン:



  1. サンプルがライン上にあり、ボタンが押された場合、列でサウンドを開始します。
  2. Playerは、別のスレッドで動作するPlaySoundクラスを実行します。


Player.java
 /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jdrum; import java.lang.reflect.Field; import java.util.logging.Level; import java.util.logging.Logger; import static jdrum.JDrum.playLine; /** * * @author nn */ public class Player extends Thread { Field field; String columnName; int[] column; public int step = 1; public int stopFlag = 0; public Player() { setDaemon(true); } public void run() { while (true) { if(JDrum.play){ try { //get column from JDrum by step 1-10 columnName = "column" + step; field = JDrum.class.getDeclaredField(columnName); field.setAccessible(true); column = (int[]) field.get(null); //play pads from column for(int i = 0;i <= 5;i++ ){ //System.out.println(columnName); if(JDrum.pads[column[i] - 1] == 1){ JDrum.playLine(column[i]); } } //next step step++; if(step == 17){ step = 1; stopFlag++; if(stopFlag == 2){ JDrum.play = false; stopFlag = 0; } } } catch (IllegalArgumentException ex) { Logger.getLogger(Player.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(Player.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchFieldException ex) { Logger.getLogger(Player.class.getName()).log(Level.SEVERE, null, ex); } catch (SecurityException ex) { Logger.getLogger(Player.class.getName()).log(Level.SEVERE, null, ex); } } //speed sleep try { sleep(JDrum.speed * 10); } catch (InterruptedException e) { // handle exception here } } } }
      
      







PlaySound.javaは、別のストリームでサウンド(クラスSound)を開始します



PlaySound.java
 /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jdrum; /** * * @author nn */ public class PlaySound extends Thread{ public Sound sound; public PlaySound(Sound sound){ this.sound = sound; } public void run() { if(sound != null){ sound.play(); } } }
      
      







Sound.java音声再生クラス



Sound.java
 /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jdrum; import java.io.File; import java.io.IOException; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.DataLine; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; /** * * @author nn */ public class Sound { public boolean playCompleted; public File file; public AudioInputStream stream; public AudioFormat format; public DataLine.Info info; public Clip clip; private final int BUFFER_SIZE = 128000; private File soundFile; private AudioInputStream audioStream; private AudioFormat audioFormat; private SourceDataLine sourceLine; public void loadFile(File file){ this.file = file; } public void play(){ if(file != null){ soundFile = file; try { audioStream = AudioSystem.getAudioInputStream(soundFile); } catch (Exception e){ e.printStackTrace(); System.exit(1); } audioFormat = audioStream.getFormat(); DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); if (!AudioSystem.isLineSupported(info)) { System.out.println("Line not supported"+ info); } try { sourceLine = (SourceDataLine) AudioSystem.getLine(info); // sourceLine.open(audioFormat); } catch (LineUnavailableException e) { e.printStackTrace(); System.exit(1); } catch (Exception e) { e.printStackTrace(); System.exit(1); } sourceLine.start(); int nBytesRead = 0; byte[] abData = new byte[BUFFER_SIZE]; while (nBytesRead != -1) { try { nBytesRead = audioStream.read(abData, 0, abData.length); } catch (IOException e) { e.printStackTrace(); } if (nBytesRead >= 0) { @SuppressWarnings("unused") int nBytesWritten = sourceLine.write(abData, 0, nBytesRead); } } /** try { Clip clip = new Clip(); int waitTime = (int)Math.ceil(clip.getMicrosecondLength()/1000.0); Thread.sleep(waitTime); } catch (InterruptedException ex) { Logger.getLogger(Sound.class.getName()).log(Level.SEVERE, null, ex); } catch (LineUnavailableException ex) { Logger.getLogger(Sound.class.getName()).log(Level.SEVERE, null, ex); } **/ sourceLine.drain(); sourceLine.close(); } } }
      
      







NetBeansを使用したMain.javaインターフェース生成はアップロードしませんが、興味深い点がいくつかあります。



Main.java
 public Main() { initComponents(); //bind load sample jTextField1.addMouseListener(new SampleEvent(1,this)); jTextField2.addMouseListener(new SampleEvent(2,this)); jTextField3.addMouseListener(new SampleEvent(3,this)); jTextField4.addMouseListener(new SampleEvent(4,this)); jTextField5.addMouseListener(new SampleEvent(5,this)); jTextField6.addMouseListener(new SampleEvent(6,this)); //bind pad click Field field; JButton dynamicButton; try { for (int buttonNum = 1; buttonNum <= 96; buttonNum++) { field = this.getClass().getDeclaredField("jButton" + buttonNum); field.setAccessible(true); dynamicButton = (JButton) field.get(this); dynamicButton.setMargin(new Insets(0, 0, 0, 0)); dynamicButton.addMouseListener(new PadEvent(buttonNum,this)); } } catch (NoSuchFieldException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (SecurityException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalArgumentException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } }
      
      







コンポーネントを初期化した後、ボタンにイベントを割り当てる必要があります。



  1. イベントは別々のクラスに配置されます。
  2. イベントを96個のボタンに割り当てるには、Reflection APIを使用します。これは、名前(name + i)でループ内のイベントを割り当てます。




SampleEvent.java
 /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jdrum; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.lang.reflect.Field; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JFileChooser; import javax.swing.JTextField; import javax.swing.filechooser.FileNameExtensionFilter; /** * * @author nn */ public class SampleEvent implements MouseListener{ public int fieldNum; Main frame; public SampleEvent(int fieldNum, Main frame){ this.fieldNum = fieldNum; this.frame = frame; } public void mouseClicked(MouseEvent evt) { if(evt.getButton() == MouseEvent.BUTTON1) { JFileChooser fileopen = new JFileChooser(); fileopen.setCurrentDirectory(new java.io.File(System.getProperty("user.dir"))); FileNameExtensionFilter filter = new FileNameExtensionFilter("wav", "wav"); fileopen.setFileFilter(filter); int ret = fileopen.showDialog(null, " "); if (ret == JFileChooser.APPROVE_OPTION) { try { File file = fileopen.getSelectedFile(); //setup file name to sample field Field field = frame.getClass().getDeclaredField("jTextField" + fieldNum); field.setAccessible(true); JTextField value = (JTextField) field.get(this); value.setText(file.getName()); Sound sound = new Sound(); sound.loadFile(file); //play JDrum.play(sound); //setup path Field f = JDrum.class.getField("line"+ fieldNum +"Sound"); f.setAccessible(true); f.set(null, sound); //System.out.print(JDrum.line1SoundFile); //set full path //System.out.println(file.getAbsolutePath()); } catch (SecurityException | IllegalArgumentException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchFieldException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } } } if(evt.getButton() == MouseEvent.BUTTON3) { try { Field field = frame.getClass().getDeclaredField("jTextField" + fieldNum); field.setAccessible(true); JTextField value = (JTextField) field.get(this); value.setText(" "); Field f = JDrum.class.getField("line"+ fieldNum +"SoundFile"); f.setAccessible(true); f.set(null, null); } catch (NoSuchFieldException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (SecurityException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalArgumentException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } } } @Override public void mousePressed(MouseEvent e) { //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseReleased(MouseEvent e) { //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseEntered(MouseEvent e) { //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseExited(MouseEvent e) { // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } }
      
      









PadEvent.java
 /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jdrum; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.lang.reflect.Field; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JButton; /** * * @author nn */ public class PadEvent implements MouseListener{ public int pudNum; Main frame; public PadEvent(int pudNum,Main frame){ this.pudNum = pudNum; this.frame = frame; } @Override public void mouseClicked(MouseEvent evt) { if(evt.getButton() == MouseEvent.BUTTON1) { Field field; JButton dynamicButton; try { // change pad color field = frame.getClass().getDeclaredField("jButton" + pudNum); field.setAccessible(true); dynamicButton = (JButton) field.get(this); //change color and play pad if(!dynamicButton.getBackground().equals(new Color(145,145,145))){ dynamicButton.setBackground(new Color(145,145,145)); }else{ dynamicButton.setBackground(null); } //play pad JDrum.playPad(pudNum); } catch (SecurityException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalArgumentException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchFieldException ex) { Logger.getLogger(PadEvent.class.getName()).log(Level.SEVERE, null, ex); } //c    1  0   0  1 //     //            //System.out.println("press" + pudNum); //c    1  0   0  1 //     //            //System.out.println("press" + pudNum); } } @Override public void mousePressed(MouseEvent e) { //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseReleased(MouseEvent e) { //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseEntered(MouseEvent e) { // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void mouseExited(MouseEvent e) { //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } }
      
      







もちろん、プログラムのアルファバージョンと同様に、エラーが発生します。

javax.sound.sampled.LineUnavailableException:PCM_SIGNED 44100.0 Hz、16ビット、ステレオ、4バイト/フレームの形式のライン、リトルエンディアンはサポートされていません。 com.sun.media.sound.DirectAudioDeviceでcom.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:121)でcom.sun.media.sound.AbstractDataLineで$ DirectDL.implOpen(DirectAudioDevice.java ∗ 13) jdrum.Sound.play(Sound.java:68)のjdrum.PlaySoundThread.run(PlaySoundThread.java:24)/home/nn/.cache/netbeans/8.2/executor-snippetsの.open(AbstractDataLine.java:153) /run.xml:53:Javaが戻りました:1 BUILD FAILED(合計時間:1分57秒)

私が理解しているように、回線が混雑しているために複数の予約とキーストロークを行った後にエラーが発生します。



このプログラムのさらなる発展は次の方向に向かっていくと思います。



  1. wavファイルの再生をmidiに変更します。
  2. メモを追加します。
  3. トラックのボリュームコントロール。




更新:

1.再生アルゴリズムを置き換えました。

2.不要なコードが削除されます。

3.同期の改善。

4.速度コントローラーを追加しました。

5.プロジェクトに再生速度の保存を追加しました。

6.停止すると、再生が先頭に移動します。

7.ループの再生位置を確認できます。



All Articles