Предисловие: у меня оборудована студия, в студию я решил докупить электронные midi ударные, инструмент с падами из линейки: medeli, akai, novation.

Для разработки на компьютере установлен Linux (Ubuntu), программное обеспечение выше упомянутых девайсов в Linux не поддерживается, а заморочки с wine и виртуальной машиной или переключение между операционными системами того не стоят.

Решил разработать простой инструмент для написания ритмов.



Скачать и протестировать программу можно по этой ссылке.

Проектирование


Проектирование начал с рисования интерфейса в NetBeans:

image

Принцип работы


Активное текстовое поле для загрузки сэмпла на линию.

16 кнопок при нажатии на которые происходит воспроизведение сэмпла установаленного на линию.

Кнопка Play воспроизводит звуки по колонкам с установаленными на них сэплами с определенной задержкой (если на линии установлен сэмпл и кнопка нажата).

Код наглядно


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();
        }
    
    }
    
    
}


Выкладывать Main.java не буду там генерация интерфейсов средствами NetBeans, только отдельные интересные моменты:

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: line with format PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian not supported. at com.sun.media.sound.DirectAudioDevice$DirectDL.implOpen(DirectAudioDevice.java:513) at com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:121) at com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:153) at jdrum.Sound.play(Sound.java:68) at jdrum.PlaySoundThread.run(PlaySoundThread.java:24) /home/nn/.cache/netbeans/8.2/executor-snippets/run.xml:53: Java returned: 1 BUILD FAILED (total time: 1 minute 57 seconds)
Ошибка возникает насколько я понял после многоклатного назначения и нажатия клавиш из за занятой линии.

Думаю дальнейшие развитие программы будет в сторону:

  1. Изменение воспроизведения wav файлов на midi.
  2. Добавления нот.
  3. Регулятор звука на дорожке.


UPDATE:
1. Заменен алгогитм воспроизведения.
2. Вычищены ненужные куски кода.
3. Улучшена синхронизация.
4. Добавлен регулятор скорости.
5. Добавлено сохранение скорости воспроизведения в проекте.
6. Стоп переводит воспроизведение на начало.
7. Видно место воспроизведения лупа.

Комментарии (14)


  1. ingumsky
    04.11.2018 19:27

    Уберите, пожалуйста, листинги под спойлеры — читать неудобно.


    1. Padaboo Автор
      04.11.2018 19:42
      +1

      Сделал


  1. Tanner
    04.11.2018 19:43

    А чем Hydrogen не понравился?


    1. Padaboo Автор
      04.11.2018 19:55

      Вообще пользуюсь в оносновмном lmms:
      Плюсы: один из самых мощьных синтезаторов звука, самый удобный редактор.
      Минусы: Сэмплов нет их нужно скачивать или синтезировать самому.

      Мне нужна простая программа или простой ударный инструмент для отстукивания ритма,
      Hydrogen уже довольно серьезная штука.


      1. resetme
        04.11.2018 22:56

        Hydrogen уже довольно серьезная штука.

        Стоит потратить на нее несколько часов, чтобы понять стоит ли вам разрабатывать что-то своё. Хотя бы она даст правильное направление для вашей программы.


        Hydrogen реально крут и не очень сложен в освоении. Потом не пожалеете потраченное на него время.


        1. Padaboo Автор
          05.11.2018 00:25

          Hydrogen у меня не работает: alsa карта steinberg ur mk II


          1. SADKO
            06.11.2018 10:54

            Карта тут скорее всего не-при-чём.


  1. ExplosiveZ
    04.11.2018 23:15

    Вот это код так код.


  1. DimPal
    05.11.2018 12:26

    Как зациклить воспроизведение? Чем менять скорость ритма? Можно как-то увидеть позицию воспроизведения?


    1. Padaboo Автор
      05.11.2018 18:39

      Ритм пока проигрывается 2 раза по колонка:
      Сделаю сегодня завтра: позицию, зацикливание и скорость.
      Есть еще какие то предложения?


    1. Padaboo Автор
      06.11.2018 00:13

      UPDATE:
      1. Заменен алгогитм воспроизведения.
      2. Вычищены ненужные куски кода.
      3. Улучшена синхронизация.
      4. Добавлен регулятор звука.
      5. Добавлено сохранение звука в проекте.
      6. Стоп переводит воспроизведение на начало.
      7. Видно место воспроизведения лупа.


      1. DimPal
        06.11.2018 08:22

        4. Добавлен регулятор звука.
        .
        Не регулятор звука, а регулятор скорости ритма. IMHO нижний порог лучше не 15, а скажем 5.
        Синхронизация точно улучшена? Поставил каждым вторым ударом snare, каждым четвертым tom — звучание не ритмичное, не совпадает с заданным рисунком, после нажатия Stop еще долго продолжает играть (Win7 SP1 Eng). Ощущение что семпл доигрывается до конца прежде чем начать следующий.


        1. Padaboo Автор
          06.11.2018 20:47

          Звуки запускаются в отдельных потоках и не равны по длинне:
          1) Один звук с длинной 0.3 секунды (Hat). оыгрывает моментально.
          2) Второй звук скажем бас начинается через 0.3 сек и длится 2 секунды.

          Сделаю пак одинаковых по длине звуков.


          Скорость 15 это наверное если на каждом паде выставлен Hat и должно получится что то вроде (Тс тс тс тс тс тс тст ст ст ст стс тс).

          Все запускается синхронно по времени но в рызных потоках.


  1. shaldnikita
    05.11.2018 18:34

    Хм, что-то похожее было в Head First Java.