jDrum studio إيقاع المحاكي

مقدمة: لدي أستوديو مجهز ، في الاستوديو قررت شراء براميل ميدي الإلكترونية ، وأدوات مع منصات من الخط: 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(); } } 


برنامج جافا.

  1. بدء الأصوات في الأعمدة إذا كانت العينة موجودة على الخط وتم الضغط على الزر.
  2. يقوم المشغل بتشغيل فئات 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 (فئة الصوت) في دفق منفصل

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 فئة استنساخ الصوت

الصوت. جافا
 /* * 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(); } } } 


لن أقوم بتحميل Main.java هناك إنشاء واجهة عن طريق NetBeans ، فقط بعض النقاط المثيرة للاهتمام:

جافا
 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 هرتز ، 16 بت ، استريو ، 4 بايت / إطار ، نهاية غير مدعومة. في com.sun.media.sound.DirectAudioDevice $ DirectDL.implOpen (DirectAudioDevice.java ∗ 13) على com.sun.media.sound.Ab abstractDataLine.open (AbstractDataLine.java:121) على com.sun.media.sound.AbractDataLine .open (AbstractDataLine.java:153) على jdrum.Sound.play (Sound.java:68) على jdrum.PlaySoundThread.run (PlaySoundThread.java:24) /home/nn/.cache/netbeans/8.2/executor-snippets /run.xml:53: تم إرجاع Java: فشل BUILD 1 (إجمالي الوقت: 1 دقيقة 57 ثانية)
ينشأ الخطأ ، كما أفهمه ، بعد عدة مواعيد وضربات مفاتيح بسبب خط مشغول.

أعتقد أن مواصلة تطوير البرنامج ستكون نحو:

  1. قم بتغيير تشغيل ملفات wav إلى midi.
  2. مضيفا الملاحظات.
  3. التحكم في مستوى الصوت على المسار.


تحديث:
1. استبدال خوارزمية التشغيل.
2. يتم مسح أجزاء من التعليمات البرمجية غير الضرورية.
3. تحسين المزامنة.
4. وأضاف تحكم السرعة.
5. إضافة توفير سرعة التشغيل في المشروع.
6. توقف يحرك التشغيل إلى البداية.
7. يمكنك رؤية موقع تشغيل الحلقة.

Source: https://habr.com/ru/post/ar428744/


All Articles