Trợ giúp với các phương trình cho phong bì ADSR theo cấp số nhân


11

Với mã ứng dụng, tôi đã triển khai một đường bao ADSR tuyến tính để định hình biên độ của đầu ra của bộ tạo dao động. Các thông số cho thời gian tấn công, phân rã và phát hành cũng như mức độ duy trì có thể được đặt trên phong bì và mọi thứ hoạt động như mong đợi.

Tuy nhiên, tôi muốn điều chỉnh các hình dạng đường nối của phong bì thành một cái gì đó giống với hầu hết các công cụ tổng hợp sử dụng cho phản ứng tự nhiên hơn: nghịch đảo theo cấp số nhân của cuộc tấn công và theo cấp số nhân cho sự phân rã và giải phóng. Tôi gặp khó khăn khi lấy công thức của mình để tính giá trị đầu ra của phong bì cho các loại hình dạng đường nối này. Để tính toán các đường dốc tuyến tính, tôi đang sử dụng dạng hai điểm, cắm các giá trị bắt đầu / kết thúc / có nguồn gốc từ các giá trị tham số đầu vào tấn công / phân rã / duy trì / giải phóng. Tôi dường như không thể tìm ra công thức chính xác cho các đường dốc theo cấp số nhân (tiêu chuẩn và nghịch đảo) bằng cách sử dụng cùng các giá trị điểm bắt đầu / kết thúc / .y x yxyxy

Tôi đã lưu một phiên tính toán đồ họa Desmos thể hiện cách tiếp cận các đường dốc tuyến tính mà tôi đã mô tả ở trên.

Nếu bất cứ ai có thể giúp chỉ cho tôi đi đúng hướng, nó sẽ được đánh giá cao.

Câu trả lời:


10

Tôi nghĩ rằng điều khiến bạn bối rối là số mũ giảm dần ( ) không bao giờ đạt đến 0, do đó, một trình tạo ADSR với các phân đoạn theo cấp số nhân thực sự sẽ bị kẹt; bởi vì nó sẽ không bao giờ đạt được giá trị mục tiêu. Ví dụ: nếu máy phát ở độ cao của pha tấn công (giả sử ) và phải hạ cánh xuống giá trị bền vững ở , thì nó không thể đi đến đó với số mũ thực sự, bởi vì số mũ thực sự đã thắng ' T phân rã thành 0,5, nó sẽ chỉ bất thường đến 0,5! y = 1 y = 0,5exy=1y=0.5

Nếu bạn nhìn vào một bộ tạo phong bì tương tự (ví dụ như mạch dựa trên 7555 mà mọi người dường như sử dụng ), bạn có thể thấy rằng trong giai đoạn tấn công, khi tụ điện đang sạc, nó "nhắm cao hơn" so với ngưỡng được sử dụng để chỉ ra điểm kết thúc của giai đoạn tấn công. Trên một mạch dựa trên (7) 555 được cung cấp bởi + 15V, Trong giai đoạn tấn công, tụ điện được sạc với bước + 15V, nhưng giai đoạn tấn công kết thúc khi đạt đến ngưỡng + 10V. Đây là một lựa chọn thiết kế, mặc dù 2/3 là "số ma thuật" được tìm thấy trong nhiều trình tạo phong bì cổ điển và đây có thể là một nhạc sĩ quen thuộc.

Một số hình dạng ADSR do "tỷ lệ nhắm" khác nhau trong quá trình sạc tụ điện

Do đó, các chức năng bạn có thể muốn xử lý không phải là số mũ, mà là các phiên bản đã thay đổi / cắt bớt / chia tỷ lệ của nó, và bạn sẽ phải đưa ra một số lựa chọn về việc bạn muốn chúng bị "đè bẹp" như thế nào.

Dù sao tôi cũng tò mò về lý do tại sao bạn đang cố gắng để có được các công thức như vậy - có thể đó là do giới hạn của công cụ bạn đang sử dụng để tổng hợp; nhưng nếu bạn đang cố gắng thực hiện những ngôn ngữ đó bằng ngôn ngữ lập trình mục đích chung (C, java, python) với một số mã chạy cho từng mẫu của phong bì và một khái niệm về "trạng thái", hãy đọc tiếp ... Bởi vì nó luôn dễ dàng hơn diễn đạt những điều như "phân khúc như vậy sẽ đi từ bất kỳ giá trị nào nó vừa đạt đến 0".

Hai lời khuyên của tôi về việc thực hiện phong bì.

Người đầu tiên khôngđể cố gắng chia tỷ lệ tất cả các độ dốc / gia số sao cho đường bao chính xác đạt đến giá trị bắt đầu và kết thúc. Ví dụ: bạn muốn một phong bì đi từ 0,8 đến 0,2 trong 2 giây, do đó bạn có thể muốn tính toán mức tăng -0,3 / giây. Đừng làm vậy. Thay vào đó, hãy chia nó thành hai bước: lấy đoạn đường nối đi từ 0 đến 1 trong 2 giây; và sau đó áp dụng một biến đổi tuyến tính ánh xạ 0 đến 0,8 và 1,0 đến 0,2. Có hai ưu điểm để làm việc theo cách này - thứ nhất là đơn giản hóa mọi tính toán mà bạn có liên quan đến thời gian phong bì đến đoạn đường nối từ 0 đến 1; thứ hai là nếu bạn thay đổi các tham số phong bì (gia số và thời gian bắt đầu / kết thúc) giữa chừng thì mọi thứ sẽ vẫn hoạt động tốt. Tốt nếu bạn đang làm việc trên một synth, vì mọi người sẽ yêu cầu có các tham số thời gian phong bì làm đích điều chế.

Thứ hai là sử dụng bảng tra cứu được tính toán trước với hình dạng phong bì. Nó nhẹ hơn về mặt tính toán, nó lấy ra nhiều chi tiết bẩn (ví dụ: bạn không phải bận tâm với số mũ không đạt 0 chính xác - cắt nó theo ý thích của bạn và giải thích nó để nó được ánh xạ tới [0, 1]), và thật dễ dàng để cung cấp một tùy chọn để thay đổi hình dạng phong bì, cho từng giai đoạn.

Đây là mã giả cho cách tiếp cận tôi mô tả.

render:
  counter += increment[stage]
  if counter > 1.0:
    stage = stage + 1
    start_value = value
    counter = 0
  position = interpolated_lookup(envelope_shape[stage], counter)
  value = start_value + (target_level[stage] - start_value) * position

trigger(state):
  if state = ON:
    stage = ATTACK
    value = 0  # for mono-style envelopes that are reset to 0 on new notes
    counter = 0
  else:
    counter = 0
    stage = RELEASE

initialization:
  target_level[ATTACK] = 1.0
  target_level[RELEASE] = 0.0
  target_level[END_OF_RELEASE] = 0.0
  increment[SUSTAIN] = 0.0
  increment[END_OF_RELEASE] = 0.0

configuration:
  increment[ATTACK] = ...
  increment[DECAY] = ...
  target_level[DECAY] = target_level[SUSTAIN] = ...
  increment[RELEASE] = ...
  envelope_shape[ATTACK] = lookup_table_exponential
  envelope_shape[DECAY] = lookup_table_exponential
  envelope_shape[RELEASE] = lookup_table_exponential

Tôi dường như giải quyết vấn đề của mình bằng cách lấy thang đo tuyến tính / phương trình hai điểm của y = ((y2 - y1) / (x2 - x1)) * (x - x1) + y1, viết lại nó bằng cách thay thế các biến x bằng e ^ x đến y = ((y2 - y1) / (e ^ x2 - e ^ x1)) * (e ^ x - e ^ x1) + y1. Phiên máy tính của tôi tại liên kết minh họa cách tiếp cận này. Có bất kỳ vấn đề nào về vấn đề này mà tôi nên biết không? Kết quả có vẻ đúng với tôi.
Gary DeReese

Đây không phải là hình dạng phong bì được tìm thấy trên các tổng hợp khác. Tùy thuộc vào vị trí thời gian / tương đối của cấp độ bắt đầu và kết thúc, nó có thể trở nên rất tuyến tính.
pichenettes

@pichenettes, bạn có thể sẵn sàng dán tập lệnh đã tạo ra những phong bì đó không?
P i

3

Đây là một câu hỏi khá cũ, nhưng tôi chỉ muốn làm nổi bật một điểm trong câu trả lời từ pichenettes:

Ví dụ: bạn muốn một phong bì đi từ 0,8 đến 0,2 trong 2 giây [...] chia nó thành hai bước: lấy một đoạn đường nối từ 0 đến 1 trong 2 giây; và sau đó áp dụng một biến đổi tuyến tính ánh xạ 0 đến 0,8 và 1,0 đến 0,2.

Quá trình này đôi khi được gọi là "nới lỏng" và trông giống như

g(x,l,u)=f(xlul)(ul)+l

trong đó và là giới hạn dưới và trên (các giá trị có thể là , và mức duy trì) và là một cái gì đó giống như . Lưu ý rằng bạn không cần điều này cho giai đoạn tấn công vì nó đã dao động từ đến .u 0 1 f ( x ) x n 0 1lu01f(x)xn01

Đây là phiên Desmos gốc, được cập nhật để sử dụng phương pháp này. Tôi đã sử dụng một hình khối ở đây, nhưng bạn * có thể sử dụng bất kỳ hình dạng nào bạn thích, miễn là tạo ra các đầu ra từ 0 đến một đầu vào nhất định từ 0 đến một.f(x)

* Tôi đoán OP có lẽ đã qua lâu, nhưng có lẽ điều này sẽ giúp người khác.


Cảm ơn bạn vì điều này, tôi đã lập trình một bộ lấy mẫu cho DAW Tôi là nhà phát triển và đã cắm vào các công thức được cung cấp trong phiên Desmos và chúng hoạt động hoàn hảo. Không còn phong bì tuyến tính khập khiễng! :)
Douglas

1

Về nhận xét của pichenettes, "Trong giai đoạn tấn công, tụ điện được sạc với bước + 15V, nhưng giai đoạn tấn công kết thúc khi đạt đến ngưỡng + 10V. Đây là một lựa chọn thiết kế, mặc dù 2/3 là" ma thuật số "được tìm thấy trong nhiều trình tạo phong bì cổ điển và đây có thể là một nhạc sĩ quen thuộc.":

Bất kỳ phong bì nào chụp cho tiệm cận 15v với mục tiêu 10v, trên thực tế, sẽ tạo ra một cuộc tấn công tuyến tính. Chỉ có 15v là tiệm cận cao nhất có sẵn một cách dễ dàng và nó đủ gần với tuyến tính. Đó là, không có gì "kỳ diệu" về điều đó, họ sẽ đi theo tuyến tính như họ có thể nhận được.

Tôi không biết có bao nhiêu synth cổ điển sử dụng 15v. Tôi nghi ngờ thường có một hoặc hai diode giảm. Mô-đun Aries cũ của tôi sử dụng 13v cho phong bì 10v và tôi chỉ tìm kiếm chip ADSR của Curtis sử dụng, tương đương, 6,5v cho phong bì 5v.


1

Mã này sẽ tạo ra các ô tương tự như của các pichenettes:

def ASD_envelope( nSamps, tAttack, tRelease, susPlateau, kA, kS, kD ):
    # number of samples for each stage
    sA = int( nSamps * tAttack )
    sD = int( nSamps * (1.-tRelease) )
    sS = nSamps - sA - sD

    # 0 to 1 over N samples, weighted with w
    def weighted_exp( N, w ):
        t = np.linspace( 0, 1, N )
        E = np.exp( w * t ) - 1
        E /= max(E)
        return E

    A = weighted_exp( sA, kA )
    S = weighted_exp( sS, kS )
    D = weighted_exp( sD, kD )

    A = A[::-1]
    A = 1.-A

    S = S[::-1]
    S *= 1-susPlateau
    S += susPlateau

    D = D[::-1]
    D *= susPlateau

    env = np.concatenate( [A,S,D] )

    # plot
    tEnv = np.linspace( 0, nSamps, len(env) )
    plt.plot( tEnv, env )
    plt.savefig( "OUT/EnvASD.png" )
    plt.close()

    return env

Tôi rất biết ơn về bất kỳ cải tiến nào, một điều có thể là một ý tưởng tốt là cho phép ba tham số cuối cùng (xác định độ dốc của từng giai đoạn trong ba giai đoạn) thay đổi trong khoảng từ 0 đến 1, trong đó 0,5 sẽ là một đường thẳng. Nhưng tôi không thể thấy tận mắt làm thế nào để làm điều đó.

Ngoài ra tôi đã không kiểm tra kỹ tất cả các trường hợp sử dụng, ví dụ nếu một giai đoạn có độ dài bằng không.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.