Python PyAudio, вывод немного хриплый. Может быть, математика

Добрый день. У меня есть небольшая проблема, которая может быть частично математической.

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

Я выбрал способ, при котором я адаптирую «время» синусоиды, чтобы у нее было время завершить все циклы. В основном y=sin(2*pift), где f*t должно быть целым числом.

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

Если бы вы прошли через это и проверить, я был бы очень признателен. Спасибо

            import pyaudio
            import numpy as np
            import matplotlib.pyplot as plt

            p = pyaudio.PyAudio()
            volume = 0.5     # range [0.0, 1.0]
            fs = 44100*4       # sampling rate, Hz, must be integer
            time = 0.1  # in seconds, may be float
            f = 400        # sine frequency, Hz, may be float
            k = np.arange(int(time*fs))
            t=np.arange(0,time,1/fs)
            start=0
            end=time




            stream = p.open(format=pyaudio.paFloat32,
                            channels=1,
                            rate=fs,
                            output=True)



            # generate samples, note conversion to float32 array
            for i in range(1000):

                start = 0
                end = 40 / f #time to acomplish whole whole cycles according to the give frequency - must be whole number

                print(len(t))
                t = np.arange(start, end, 1 / fs)
                samples = (np.sin(2*np.pi*f*t)).astype(np.float32)

                print(samples[0],samples[-1]) # The main problem. I need first and last elements in the sample to be zero.
                                            # Problem is that last element is only close to zero, which make the sound not so smooth

                #print(start+i,end+i)
                #print(samples)  # # # # # Shows first and last element

                f+=1

                # for paFloat32 sample values must be in range [-1.0, 1.0]


            # play. May repeat with different volume values (if done interactively)
                stream.write(volume*samples)

            stream.stop_stream()
            stream.close()

            p.terminate()

person Martin    schedule 02.09.2016    source источник
comment
что, если вы позволите end=(40/f)+1/fs? Я думаю, ваша проблема в том, что np.arange не включает конечное значение, но вы хотите.   -  person Jeremy Kahan    schedule 02.09.2016
comment
Это было первое, что пришло мне в голову, но ситуация не изменилась. Ошибка составляет от 1 сотой до 1 тысячной по сравнению с максимальной амплитудой 1. Это небольшая ошибка, но она достаточно велика, чтобы ее можно было распознать, и она достаточно велика, чтобы быть ошибкой простой неточности Пи.   -  person Martin    schedule 02.09.2016
comment
Я предполагаю, что вы рассмотрели настройку всех трех диапазонов и все равно не помогли. Это хак, но вы можете добавить 0 в свой массив. Если это ошибка точности, возможно, использование float64 поможет, возможно, не только в значении синуса, но и в вычислении 1/fs.   -  person Jeremy Kahan    schedule 03.09.2016
comment
добавляя все эти 1/fs, я чувствую накопление ошибки округления. Вероятно, лучше умножить 1 на термин, а затем разделить на fs (чтобы выполнить всю аранжировку вручную, что также даст вам более жесткий контроль над конечным значением).   -  person Jeremy Kahan    schedule 03.09.2016
comment
Так что, в конце концов, я не понял, почему последний элемент не равен нулю. Однако я нашел, почему это немного потрескалось. Дело было в том, чтобы сократить время от Start до End 1/fs. В противном случае волна закончилась бы нулем, а новая волна снова началась бы с нуля, что прерывает непрерывность. В любом случае спасибо за советы   -  person Martin    schedule 03.09.2016


Ответы (1)


Функция синуса повторяется каждый раз, когда число кратно 2*pi*N, где N — целое число. IOW, sin(2*pi) == sin(2*pi*2) == sin(2*pi*3) и так далее.

Типичный метод генерации сэмплов определенной частоты — sin(2*pi*i*freq/sampleRate), где i — номер семпла.

Далее следует, что синус будет повторяться только при значениях i, таких что i*freq/sampleRate точно равно целому числу (я игнорирую фазовые сдвиги).

Конечным результатом является то, что некоторые комбинации частота/частота дискретизации могут повторяться только после одного цикла (1 кГц при 48kSr), тогда как для повторения других может потребоваться очень много времени (997 Гц при 48kSr).

Нет необходимости менять частоты при точном пересечении нуля, чтобы избежать сбоев. Лучший подход таков:

  1. Вычислите приращение фазы для желаемой частоты как phaseInc = 2*pi*freq/sampleRate
  2. Для каждой выходной выборки вычислить выходную выборку из текущей фазы. y = sin(phase)
  3. Обновите фазу с шагом фазы: phase += phaseInc
  4. Повторите 2-3 для желаемого количества образцов.
  5. Перейти к первому шагу, чтобы изменить частоту

Если вы настаиваете на изменении при пересечении нуля, просто сделайте это на ближайшем образце, где фаза пересекает кратное 2*pi.

person jaket    schedule 10.09.2016