Воспроизведение 2 разных частот поочередно в Java

Я новичок в Java Sounds. Я хочу воспроизводить 2 разные частоты поочередно по 1 секунде в цикле в течение определенного времени. Например, если у меня есть 2 частоты 440 Гц и 16000 Гц, а период времени составляет 10 секунд, то за каждую «четную» секунду проигрывается 440 Гц, а за каждую «нечетную» секунду 16000 Гц, т.е. по 5 секунд попеременно.

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

Я буду очень признателен, если кто-то может помочь мне в этом. Спасибо.

Я также прилагаю этот одночастотный код для справки.

  import java.nio.ByteBuffer;
  import java.util.Scanner;
  import javax.sound.sampled.*;

  public class Audio {

   public static void main(String[] args) throws InterruptedException, LineUnavailableException {
    final int SAMPLING_RATE = 44100;            // Audio sampling rate
    final int SAMPLE_SIZE = 2;                  // Audio sample size in bytes

    Scanner in = new Scanner(System.in);
    int time = in.nextInt();                      //Time specified by user in seconds
    SourceDataLine line;
    double fFreq = in.nextInt();                         // Frequency of sine wave in hz

    //Position through the sine wave as a percentage (i.e. 0 to 1 is 0 to 2*PI)
    double fCyclePosition = 0;

    //Open up audio output, using 44100hz sampling rate, 16 bit samples, mono, and big 
    // endian byte ordering
    AudioFormat format = new AudioFormat(SAMPLING_RATE, 16, 1, true, true);
    DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

    if (!AudioSystem.isLineSupported(info)) {
        System.out.println("Line matching " + info + " is not supported.");
        throw new LineUnavailableException();
    }

    line = (SourceDataLine) AudioSystem.getLine(info);
    line.open(format);
    line.start();

    // Make our buffer size match audio system's buffer
    ByteBuffer cBuf = ByteBuffer.allocate(line.getBufferSize());

    int ctSamplesTotal = SAMPLING_RATE * time;         // Output for roughly user specified time in seconds

    //On each pass main loop fills the available free space in the audio buffer
    //Main loop creates audio samples for sine wave, runs until we tell the thread to exit
    //Each sample is spaced 1/SAMPLING_RATE apart in time
    while (ctSamplesTotal > 0) {
        double fCycleInc = fFreq / SAMPLING_RATE;  // Fraction of cycle between samples

        cBuf.clear();                            // Discard samples from previous pass

        // Figure out how many samples we can add
        int ctSamplesThisPass = line.available() / SAMPLE_SIZE;
        for (int i = 0; i < ctSamplesThisPass; i++) {
            cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * fCyclePosition)));

            fCyclePosition += fCycleInc;
            if (fCyclePosition > 1) {
                fCyclePosition -= 1;
            }
        }

        //Write sine samples to the line buffer.  If the audio buffer is full, this will 
        // block until there is room (we never write more samples than buffer will hold)
        line.write(cBuf.array(), 0, cBuf.position());
        ctSamplesTotal -= ctSamplesThisPass;     // Update total number of samples written 

        //Wait until the buffer is at least half empty  before we add more
        while (line.getBufferSize() / 2 < line.available()) {
            Thread.sleep(1);
        }
    }

    //Done playing the whole waveform, now wait until the queued samples finish 
    //playing, then clean up and exit
    line.drain();
    line.close();
}

}


person Vibhav Chaddha    schedule 14.11.2016    source источник
comment
Я буду очень признателен, если кто-нибудь поможет мне в этом. Я буду очень признателен, если вы зададите вопрос. Кстати 1) Я думаю, что звук в диапазоне МГц будет неслышен для людей и не сможет быть обработан любым звуковым форматом, который поддерживает Java. 2) См. Обнаружение/исправление висящей закрывающей скобки блока кода для проблемы, которую я больше не мог беспокоить. фиксация.   -  person Andrew Thompson    schedule 14.11.2016
comment
@AndrewThompson Извините за путаницу. Собственно, вчера тоже искал оперативку для своего ноута, 1600Mhz. Так вот в чем была причина такой глупой ошибки.   -  person Vibhav Chaddha    schedule 15.11.2016


Ответы (2)


Лучше всего, вероятно, создать Clips, как показано в примере кода ниже. Тем не менее, диапазон МГц обычно не слышен — похоже, у вас опечатка в вашем вопросе. Если это не опечатка, у вас возникнут проблемы с Mr. Найквист.

Еще один совет: никто не использует венгерскую нотацию в Java.

import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;

public class AlternatingTones {

    public static void main(final String[] args) throws LineUnavailableException, InterruptedException {

        final Clip clip0 = createOneSecondClip(440f);
        final Clip clip1 = createOneSecondClip(16000f);

        clip0.addLineListener(event -> {
            if (event.getType() == LineEvent.Type.STOP) {
                clip1.setFramePosition(0);
                clip1.start();
            }
        });
        clip1.addLineListener(event -> {
            if (event.getType() == LineEvent.Type.STOP) {
                clip0.setFramePosition(0);
                clip0.start();
            }
        });
        clip0.start();

        // prevent JVM from exiting
        Thread.sleep(10000000);
    }

    private static Clip createOneSecondClip(final float frequency) throws LineUnavailableException {
        final Clip clip = AudioSystem.getClip();
        final AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100f, 16, 1, 2, 44100, true);
        final ByteBuffer buffer = ByteBuffer.allocate(44100 * format.getFrameSize());
        final ShortBuffer shortBuffer = buffer.asShortBuffer();
        final float cycleInc = frequency / format.getFrameRate();
        float cyclePosition = 0f;
        while (shortBuffer.hasRemaining()) {
            shortBuffer.put((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * cyclePosition)));
            cyclePosition += cycleInc;
            if (cyclePosition > 1) {
                cyclePosition -= 1;
            }
        }
        clip.open(format, buffer.array(), 0, buffer.capacity());
        return clip;
    }
}    
person Hendrik    schedule 14.11.2016
comment
Я не согласен с преувеличением того, что никто не использует венгерскую нотацию в Java. Я помню, как читал статьи о том, как это может быть полезно, и фактически сам использую его как способ концептуально показать, что элементы графического интерфейса связаны (например, метка, ползунок и управляющая переменная). Простой поиск показывает, что существует много таких статей. Например: developer.com/java/ent/article.php/615891/ Кроме того, маркировка переменных clip1, clip2 является довольно слабой альтернативой и довольно запутанной, когда также приходится бороться с переменной clip. - person Phil Freihofner; 15.11.2016
comment
@ Хендрик Большое спасибо. Ваш ответ был действительно полезен. - person Vibhav Chaddha; 15.11.2016
comment
@ Фил Венгерский или нет - как и все имена переменных, конечно, вопрос вкуса. Тем не менее, Java строго типизирована. Компилятор применяет правила типов, и любой современный API выделяет цветом разницу между локальными переменными и членами. Во всяком случае. Это не то, из-за чего следует бороться. Есть разные мнения. Я думаю, что все действительны. Я предлагаю прочитать код некоторых популярных проектов с открытым исходным кодом, чтобы прийти к мнению, которое соответствует мейнстриму, потому что это помогает в повседневной работе использовать те же соглашения, что и большинство других людей. - person Hendrik; 15.11.2016
comment
@hendrik Спасибо, что помог мне. Я очень ценю это. И если мне понадобится дополнительная помощь, я обязательно попрошу вас. Еще раз спасибо. - person Vibhav Chaddha; 15.11.2016
comment
@hendrik Если я хочу, чтобы пользователь указал время между каждой частотой в приведенном выше коде, то как мне это сделать? Например: если я запускаю приведенный выше код, он переключается между частотами каждую секунду. Что, если я хочу, чтобы пользователь указал время, например 2 секунды или 3 секунды и т. д., в течение которого частоты переключаются. - person Vibhav Chaddha; 21.11.2016
comment
Вы пробовали звонить loop(numberOfSeconds) вместо start()? Это работает? Сам не пробовал. - person Hendrik; 21.11.2016
comment
@hendrik Я пробовал это раньше, но сделал какую-то глупую ошибку, и поэтому это не сработало. Но теперь это работает. Большое спасибо. Еще одна вещь, я хочу записать его в формате .wav. Можете ли вы также помочь мне в этом. - person Vibhav Chaddha; 21.11.2016
comment
Вы можете посмотреть на stackoverflow .com/questions/3297749/ или что-то подобное. - person Hendrik; 21.11.2016
comment
@hendrik На самом деле я уже видел ссылку, которую вы упомянули выше, а также несколько других, но мне кажется, что это немного сложно понять. - person Vibhav Chaddha; 21.11.2016
comment
Затем вам следует задать новый вопрос Stackoverflow о том, чего вы не получили. - person Hendrik; 22.11.2016
comment
@hendrik stackoverflow.com/questions/40722139/ - person Vibhav Chaddha; 22.11.2016

Я бы использовал метод подсчета кадров при выводе в SourceDataLine. Когда вы записали кадры длительностью в одну секунду, переключите частоты. Это даст гораздо лучшую точность синхронизации, чем попытка возиться с клипами.

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

При выводе в SourceDataLine должен быть шаг, на котором вы конвертируете короткое значение (-32768..+32767) в два байта в соответствии с 16-битной кодировкой, указанной в вашем аудиоформате. Я не вижу, где это делается в вашем коде. [EDIT: можно увидеть, где это делает метод putShort(), хотя он работает только для BigEndian, а не для более распространенного LittleEndian.]

Вы ознакомились с руководством по Java Sound Trail?

person Phil Freihofner    schedule 15.11.2016
comment
Фактически, этот код является одночастотным. Хотя вы правы, я не совсем понял код. Есть несколько вещей, которые я никогда не использовал в своей жизни до этого. Так что, если вы можете взять на себя труд объяснить мне все это, то это было бы большим подспорьем. Спасибо. - person Vibhav Chaddha; 15.11.2016
comment
Что я рекомендую вам, так это попробовать звуковую тропу, которую я связал. На данный момент можно пропустить части, посвященные MIDI и интерфейсам поставщика услуг. Сделайте все возможное, чтобы прочитать третье руководство по воспроизведению аудио. Это сложно, но не торопитесь и делайте тестовые программы по ходу дела. С помощью этого словаря понятий в качестве справочного материала будет легче задавать более конкретные вопросы, а другим — отвечать на них. Изучение аудио, возможно, так же сложно, как изучение расширенной графики, и требует определенных усилий и базовых знаний. - person Phil Freihofner; 15.11.2016
comment
Это даст гораздо лучшую точность синхронизации, чем попытка возиться с клипами. ‹- Вероятно, это так и стоит задуматься. - person Hendrik; 15.11.2016
comment
@PhilFreihofner Спасибо, что помогли мне. Я очень ценю это. И если мне понадобится дополнительная помощь, я обязательно попрошу вас. Еще раз спасибо. - person Vibhav Chaddha; 15.11.2016