Eclipse не находит основной класс во время экспорта проекта в виде исполняемого файла jar

#java #eclipse

Вопрос:

У меня такое чувство, что это будет тривиальный вопрос, но это заставляет меня биться головой о стену.

Мне нужна была программа на Java, которая могла бы записывать звук так быстро, как только это возможно. Я искал какой-то уже существующий код, я нашел его и внес ровно одно изменение в код: я гарантировал, что первым делом после создания графического интерфейса будет сама Java, чтобы нажать кнопку записи.

Ниже приведен измененный основной метод, найденный в классе SwingSoundRecorder :

     public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                SwingSoundRecorder GUI = null;
                GUI = new SwingSoundRecorder();
                GUI.setVisible(true);
                
                // This line ensures the GUI will start recording immediately
                GUI.buttonPlay.doClick();
            }
        });
    }
 

Сделав это, я попытался экспортировать его как исполняемый файл Java. Поскольку я использую Eclipse, я подумал, что это будет очень просто: нужно просто указать, где вы хотите получить файл .jar и что является основным классом.

Ну, по-видимому, Java не признает SwingSoundRecorder , что у нее есть основной метод для использования. И я действительно не понимаю, почему.

Есть идеи? Какая-нибудь помощь?


Редактировать: полный код всех четырех задействованных классов, как справедливо хотел @аниш Шарма:

 public class SwingSoundRecorder extends JFrame implements ActionListener {

    /**
     * 
     */
    private static final long serialVersionUID = 2747541100831140840L;
    private JButton buttonRecord = new JButton("Record");
    private JButton buttonPlay = new JButton("Play");
    private JLabel labelRecordTime = new JLabel("Record Time: 00:00:00");

    private SoundRecordingUtil recorder = new SoundRecordingUtil();
    private AudioPlayer player = new AudioPlayer();
    private Thread playbackThread;
    private RecordTimer timer;

    private boolean isRecording = false;
    private boolean isPlaying = false;

    private String saveFilePath;

    // Icons used for buttons
    private ImageIcon iconRecord = new ImageIcon(getClass().getResource(
            "/net/codejava/sound/images/Record.gif"));
    private ImageIcon iconStop = new ImageIcon(getClass().getResource(
            "/net/codejava/sound/images/Stop.gif"));
    private ImageIcon iconPlay = new ImageIcon(getClass().getResource(
            "/net/codejava/sound/images/Play.gif"));
    

    /**
     * launch the program
     * 
     */
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                SwingSoundRecorder GUI = null;
                GUI = new SwingSoundRecorder();
                GUI.setVisible(true);
                
                // This line ensures the GUI will start recording immediately
                GUI.buttonPlay.doClick();
            }
        });
    }

    
    
    
    
    
    
    
    
    
    
    
    

    public SwingSoundRecorder() {
        super("Swing Sound Recorder");
        setLayout(new FlowLayout());

        buttonRecord.setFont(new Font("Sans", Font.BOLD, 14));
        buttonRecord.setIcon(iconRecord);
        buttonPlay.setFont(new Font("Sans", Font.BOLD, 14));
        buttonPlay.setIcon(iconPlay);
        buttonPlay.setEnabled(false);
        labelRecordTime.setFont(new Font("Sans", Font.BOLD, 12));

        add(buttonRecord);
        add(labelRecordTime);
        add(buttonPlay);

        buttonRecord.addActionListener(this);
        buttonPlay.addActionListener(this);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    /**
     * Handle click events on the buttons.
     */
    @Override
    public void actionPerformed(ActionEvent event) {
        JButton button = (JButton) event.getSource();
        if (button == buttonRecord) {
            if (!isRecording) {
                startRecording();
            } else {
                stopRecording();
            }

        } else if (button == buttonPlay) {
            if (!isPlaying) {
                playBack();
            } else {
                stopPlaying();
            }
        }
    }

    /**
     * Start recording sound, the time will count up.
     */
    private void startRecording() {
        Thread recordThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    isRecording = true;
                    buttonRecord.setText("Stop");
                    buttonRecord.setIcon(iconStop);
                    buttonPlay.setEnabled(false);

                    recorder.start();

                } catch (LineUnavailableException ex) {
                    JOptionPane.showMessageDialog(SwingSoundRecorder.this,
                            "Error", "Could not start recording sound!",
                            JOptionPane.ERROR_MESSAGE);
                    ex.printStackTrace();
                }
            }
        });
        recordThread.start();
        timer = new RecordTimer(labelRecordTime);
        timer.start();
    }

    /**
     * Stop recording and save the sound into a WAV file
     */
    private void stopRecording() {
        isRecording = false;
        try {
            timer.cancel();
            buttonRecord.setText("Record");
            buttonRecord.setIcon(iconRecord);
            
            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

            recorder.stop();

            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

            saveFile();

        } catch (IOException ex) {
            JOptionPane.showMessageDialog(SwingSoundRecorder.this, "Error",
                    "Error stopping sound recording!",
                    JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
        }
    }

    /**
     * Start playing back the sound.
     */
    private void playBack() {
        timer = new RecordTimer(labelRecordTime);
        timer.start();
        isPlaying = true;
        playbackThread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {

                    buttonPlay.setText("Stop");
                    buttonPlay.setIcon(iconStop);
                    buttonRecord.setEnabled(false);

                    player.play(saveFilePath);
                    timer.reset();

                    buttonPlay.setText("Play");
                    buttonRecord.setEnabled(true);
                    buttonPlay.setIcon(iconPlay);
                    isPlaying = false;

                } catch (UnsupportedAudioFileException ex) {
                    ex.printStackTrace();
                } catch (LineUnavailableException ex) {
                    ex.printStackTrace();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }

            }
        });

        playbackThread.start();
    }

    /**
     * Stop playing back.
     */
    private void stopPlaying() {
        timer.reset();
        timer.interrupt();
        player.stop();
        playbackThread.interrupt();
    }

    /**
     * Save the recorded sound into a WAV file.
     * 
     * @author Nam Ha Minh, source is <a href="https://www.codejava.net/coding/capture-and-record-sound-into-wav-file-with-java-sound-api">here</a>
     */
    private void saveFile() {
        JFileChooser fileChooser = new JFileChooser();
        FileFilter wavFilter = new FileFilter() {
            @Override
            public String getDescription() {
                return "Sound file (*.WAV)";
            }

            @Override
            public boolean accept(File file) {
                if (file.isDirectory()) {
                    return true;
                } else {
                    return file.getName().toLowerCase().endsWith(".wav");
                }
            }
        };

        fileChooser.setFileFilter(wavFilter);
        fileChooser.setAcceptAllFileFilterUsed(false);

        int userChoice = fileChooser.showSaveDialog(this);
        if (userChoice == JFileChooser.APPROVE_OPTION) {
            saveFilePath = fileChooser.getSelectedFile().getAbsolutePath();
            if (!saveFilePath.toLowerCase().endsWith(".wav")) {
                saveFilePath  = ".wav";
            }

            File wavFile = new File(saveFilePath);

            try {
                recorder.save(wavFile);

                JOptionPane.showMessageDialog(SwingSoundRecorder.this,
                        "Saved recorded sound to:n"   saveFilePath);

                buttonPlay.setEnabled(true);

            } catch (IOException ex) {
                JOptionPane.showMessageDialog(SwingSoundRecorder.this, "Error",
                        "Error saving to sound file!",
                        JOptionPane.ERROR_MESSAGE);
                ex.printStackTrace();
            }
        }
    }
    
    
}
 
 public class SoundRecordingUtil {
    private static final int BUFFER_SIZE = 4096;
    private ByteArrayOutputStream recordBytes;
    private TargetDataLine audioLine;
    private AudioFormat format;

    private boolean isRunning;

    /**
     * Defines a default audio format used to record
     */
    AudioFormat getAudioFormat() {
        float sampleRate = 44100;
        int sampleSizeInBits = 16;
        int channels = 2;
        boolean signed = true;
        boolean bigEndian = true;
        return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,
                bigEndian);
    }

    /**
     * Start recording sound.
     * @throws LineUnavailableException if the system does not support the specified 
     * audio format nor open the audio data line.
     */
    public void start() throws LineUnavailableException {
        format = getAudioFormat();
        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);

        // checks if system supports the data line
        if (!AudioSystem.isLineSupported(info)) {
            throw new LineUnavailableException(
                    "The system does not support the specified format.");
        }

        audioLine = AudioSystem.getTargetDataLine(format);

        audioLine.open(format);
        audioLine.start();

        byte[] buffer = new byte[BUFFER_SIZE];
        int bytesRead = 0;

        recordBytes = new ByteArrayOutputStream();
        isRunning = true;

        while (isRunning) {
            bytesRead = audioLine.read(buffer, 0, buffer.length);
            recordBytes.write(buffer, 0, bytesRead);
        }
    }

    /**
     * Stop recording sound.
     * @throws IOException if any I/O error occurs.
     */
    public void stop() throws IOException {
        isRunning = false;
        
        if (audioLine != null) {
            audioLine.drain();
            audioLine.close();
        }
    }

    /**
     * Save recorded sound data into a .wav file format.
     * @param wavFile The file to be saved.
     * @throws IOException if any I/O error occurs.
     * @throws UnsupportedAudioFileException 
     */
    public void save(File wavFile) throws IOException {
        byte[] audioData = recordBytes.toByteArray();
        ByteArrayInputStream bais = new ByteArrayInputStream(audioData);
        AudioInputStream audioInputStream = new AudioInputStream(bais, format,
                audioData.length / format.getFrameSize());      
        AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, wavFile);

        audioInputStream.close();
        recordBytes.close();
    }
}
 
 public class RecordTimer extends Thread {
    private DateFormat dateFormater = new SimpleDateFormat("HH:mm:ss"); 
    private boolean isRunning = false;
    private boolean isReset = false;
    private long startTime;
    private JLabel labelRecordTime;
    
    RecordTimer(JLabel labelRecordTime) {
        this.labelRecordTime = labelRecordTime;
    }
    
    public void run() {
        isRunning = true;
        
        startTime = System.currentTimeMillis();
        
        while (isRunning) {
            try {
                Thread.sleep(1000);
                labelRecordTime.setText("Record Time: "   toTimeString());
            } catch (InterruptedException ex) {
                ex.printStackTrace();
                if (isReset) {
                    labelRecordTime.setText("Record Time: 00:00:00");
                    isRunning = false;      
                    break;
                }
            }
        }
    }
    
    /**
     * Cancel counting record/play time.
     */
    void cancel() {
        isRunning = false;      
    }
    
    /**
     * Reset counting to "00:00:00"
     */
    void reset() {
        isReset = true;
        isRunning = false;
    }
    
    /**
     * Generate a String for time counter in the format of "HH:mm:ss"
     * @return the time counter
     */
    private String toTimeString() {
        long now = System.currentTimeMillis();
        Date current = new Date(now - startTime);
        dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
        String timeCounter = dateFormater.format(current);
        return timeCounter;
    }
}
 
 public class AudioPlayer implements LineListener {
    /**
     * this flag indicates whether the playback completes or not.
     */
    boolean playCompleted;
    
    /**
     * this flag indicates whether the playback is stopped or not.
     */
    boolean isStopped;

    /**
     * Play a given audio file.
     * 
     * @param audioFilePath
     *            Path of the audio file.
     * @throws IOException
     * @throws UnsupportedAudioFileException
     * @throws LineUnavailableException
     */
    void play(String audioFilePath) throws UnsupportedAudioFileException,
            IOException, LineUnavailableException {
        File audioFile = new File(audioFilePath);

        AudioInputStream audioStream = AudioSystem
                .getAudioInputStream(audioFile);

        AudioFormat format = audioStream.getFormat();

        DataLine.Info info = new DataLine.Info(Clip.class, format);

        Clip audioClip = (Clip) AudioSystem.getLine(info);

        audioClip.addLineListener(this);

        audioClip.open(audioStream);

        audioClip.start();
        
        playCompleted = false;
        
        while (!playCompleted) {
            // wait for the playback completes
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
                if (isStopped) {
                    audioClip.stop();
                    break;
                }
            }
        }

        audioClip.close();

    }

    /**
     * Stop playing back.
     */
    public void stop() {
        isStopped = true;
    }
    
    /**
     * Listens to the audio line events to know when the playback completes.
     */
    @Override
    public void update(LineEvent event) {
        LineEvent.Type type = event.getType();
        if (type == LineEvent.Type.STOP) {
            playCompleted = true;
        }
    }
}
 

Комментарии:

1. пожалуйста, вставьте полный код класса

2. Вы пытались вставить в net.codejava.sound.SwingSoundRecorder текстовое поле основного класса «вручную»?

3. @анишшарма Готово

4. @g00se Вы не можете в Eclipse: есть поле со списком, в котором должен отображаться каждый класс Java с основным методом, затем вы выбираете тот, который является основным классом вашего проекта. Я установил второе затмение только для того, чтобы этот проект был единственным в моем рабочем пространстве, и поле со списком пусто.

5. Ну, я только что попробовал, и вы можете в той версии, в которой я это пробовал (Версия: 2020-03 (4.15.0)). Я тоже обнаружил, что не могу перейти к правильному основному классу. Возможно, метаданные Eclipse в их файлах проекта являются неполными… (Я не использую IDE, когда могу этого избежать)