-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathEnvelope.vb
200 lines (167 loc) · 6.59 KB
/
Envelope.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
Imports System.ComponentModel.DataAnnotations
Imports System.Threading
''' <summary>
''' Represents the limiting curvature that should be applied to the signal generated by an <see cref="Oscillator"/>.
''' </summary>
Public Class Envelope
Implements IDisposable
''' <summary>
''' Structure that represents when a limiting value change should be applied.
''' </summary>
Public Class EnvelopePoint
''' <summary>
''' Gets or sets the attenuation value.
''' </summary>
''' <returns><see cref="Double"/></returns>
<RangeAttribute(0.0, 1.0)> Public Property Volume As Double ' 0 - 1
''' <summary>
''' Gets or sets the time (in milliseconds) when to apply the <see cref="Volume"/>.
''' </summary>
''' <returns><see cref="Integer"/></returns>
<RangeAttribute(0, Integer.MaxValue)> Public Property Duration As Integer ' milliseconds
Public Sub New(volume As Double, duration As Integer)
Me.Volume = volume
Me.Duration = duration
End Sub
End Class
Public Enum EnvelopeSteps
Idle = 0
Attack = 1
Decay = 2
Sustain = 3
Release = 4
End Enum
Private mVolume As Double
Public Property Attack As EnvelopePoint
Public Property Decay As EnvelopePoint
Public Property Sustain As EnvelopePoint
Public Property Release As EnvelopePoint
Private lastVolume As Double
Private mEnvStep As EnvelopeSteps
Private abortThreads As Boolean
Private startTicks As Long
Private Const tickToMs As Long = 10000
''' <summary>
''' This event is triggered when a new <see cref="EnvelopePoint"/> is applied.
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Public Event EnvelopStepChanged(sender As Object, e As EventArgs)
Public Sub New()
mEnvStep = EnvelopeSteps.Idle
Me.Attack = New EnvelopePoint(1, 1)
Me.Decay = New EnvelopePoint(1, 1)
Me.Sustain = New EnvelopePoint(1, Integer.MaxValue)
Me.Release = New EnvelopePoint(0, 1)
Task.Run(AddressOf MainLoop)
End Sub
Public Sub New(attack As EnvelopePoint, decay As EnvelopePoint, sustain As EnvelopePoint, release As EnvelopePoint)
Me.New()
Me.Attack = attack
Me.Decay = decay
Me.Release = release
End Sub
''' <summary>
''' Gets the current attenuation value of the envelop, based on its current <see cref="EnvelopePoint"/>
''' </summary>
''' <returns><see cref="Double"/> </returns>
<RangeAttribute(0.0, 1.0)>
Public ReadOnly Property Volume As Double
Get
Return mVolume
End Get
End Property
''' <summary>
''' Gets or sets the type of <see cref="EnvelopePoint"/> being applied.
''' </summary>
''' <returns><see cref="EnvelopeSteps"/></returns>
Public Property EnvelopStep As EnvelopeSteps
Get
Return mEnvStep
End Get
Protected Set(value As EnvelopeSteps)
mEnvStep = value
lastVolume = mVolume
Reset()
RaiseEvent EnvelopStepChanged(Me, New EventArgs())
End Set
End Property
''' <summary>
''' Begins the envelop processing, starting with the <see cref="EnvelopePoint"/> defined as <see cref="EnvelopeSteps.Attack"/>
''' </summary>
Public Sub Start()
mVolume = 0
EnvelopStep = EnvelopeSteps.Attack
End Sub
''' <summary>
''' Triggers the envelop processing for the <see cref="EnvelopePoint"/> defined as <see cref="EnvelopeSteps.Release"/>
''' </summary>
Public Sub [Stop]()
EnvelopStep = EnvelopeSteps.Release
End Sub
Public Sub Reset()
startTicks = Now.Ticks
End Sub
Private Sub MainLoop()
Dim ep As New EnvelopePoint(0, 0)
Dim lastEp As EnvelopeSteps = EnvelopeSteps.Idle
Dim elapsedMs As Long
Do
Thread.Sleep(1)
If mEnvStep = EnvelopeSteps.Idle Then
Else
elapsedMs = (Now.Ticks - startTicks) / tickToMs
If mEnvStep <> lastEp Then
Select Case mEnvStep
Case EnvelopeSteps.Attack : ep = Attack
Case EnvelopeSteps.Decay : ep = Decay
Case EnvelopeSteps.Sustain : ep = Sustain
Case EnvelopeSteps.Release : ep = Release
End Select
If lastEp = EnvelopeSteps.Idle Then elapsedMs = 0
lastEp = mEnvStep
Reset()
End If
' Linear interpolation
mVolume = Math.Min(1, (ep.Duration - elapsedMs) / ep.Duration * lastVolume +
elapsedMs / ep.Duration * ep.Volume)
If elapsedMs >= ep.Duration Then
Select Case mEnvStep
Case EnvelopeSteps.Attack : EnvelopStep = EnvelopeSteps.Decay
Case EnvelopeSteps.Decay : EnvelopStep = EnvelopeSteps.Sustain
Case EnvelopeSteps.Sustain : EnvelopStep = EnvelopeSteps.Release
Case EnvelopeSteps.Release : EnvelopStep = EnvelopeSteps.Idle
End Select
End If
End If
Loop Until abortThreads
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
abortThreads = True
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' ' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
' TODO: uncomment the following line if Finalize() is overridden above.
' GC.SuppressFinalize(Me)
End Sub
#End Region
End Class