1
00:00:00,012 --> 00:00:04,877
Hi and welcome to module 5.11.
We have come a long way since the bare

2
00:00:04,877 --> 00:00:10,685
bone definition of a filter and it is now
time to put to use everything that we've

3
00:00:10,685 --> 00:00:16,105
learned before and build a real time
signal processing system on your pc.

4
00:00:16,105 --> 00:00:21,701
So here we will review the technicalities
involved in implementing real time

5
00:00:21,701 --> 00:00:25,552
processing.
On a general architecture and then we will

6
00:00:25,552 --> 00:00:30,778
review the code that will allow you to
implement real time guitar effects on your

7
00:00:30,778 --> 00:00:33,620
PC.
I hope you're going to have fun with this

8
00:00:33,620 --> 00:00:36,516
one.
Hi and welcome to module 5.11 of digital

9
00:00:36,516 --> 00:00:40,519
signal processing.
In this module we'll talk about real time

10
00:00:40,519 --> 00:00:46,135
signal processing, with a particular focus
on general purpose architectures such as

11
00:00:46,135 --> 00:00:49,651
your PC.
We will first examine how data transfer

12
00:00:49,651 --> 00:00:54,270
take place between your PC and peripherals
such as the sound card.

13
00:00:54,270 --> 00:00:59,606
We will talk about multiple buffering
which is the technique to ensure smooth

14
00:00:59,606 --> 00:01:03,973
data communication between the CPU and the
external devices.

15
00:01:03,974 --> 00:01:08,810
We will look at the general implementation
framework from the point of view of the

16
00:01:08,810 --> 00:01:11,502
API.
And then we will look at how to implement

17
00:01:11,502 --> 00:01:14,396
some guitar effects for real
time[INAUDIBLE].

18
00:01:14,396 --> 00:01:19,022
When we talk about real time signal
processing, we indicate a situation where,

19
00:01:19,022 --> 00:01:22,401
the maximum time that we can spend to
process one sample.

20
00:01:22,402 --> 00:01:25,165
Is indicated by a system clock of period t
s.

21
00:01:25,165 --> 00:01:28,771
We really haven't talked about sample and
interpolation.

22
00:01:28,771 --> 00:01:33,565
We will do that in the next module, but
you already know that to process a real

23
00:01:33,565 --> 00:01:37,600
world signal, we will have to record that
in the form of samples.

24
00:01:37,600 --> 00:01:40,428
And we will record a sample every t s
seconds.

25
00:01:40,428 --> 00:01:44,784
Then we will process this sample in a
causal filter, because we are in a

26
00:01:44,784 --> 00:01:48,942
real-time situation.
And the maximum amount of time that we can

27
00:01:48,942 --> 00:01:52,821
spend on the processing of one sample is
again TS seconds.

28
00:01:52,821 --> 00:01:57,654
And then, every TS seconds, we will have
to play out the output sample.

29
00:01:57,654 --> 00:02:01,197
So everything needs to happen in at most
TS seconds.

30
00:02:01,197 --> 00:02:04,487
Let's look in more detail at the output
process.

31
00:02:04,487 --> 00:02:09,420
We have a series of samples, X of M.
We have a sound card, which is teh device

32
00:02:09,420 --> 00:02:14,181
that will bridge our digital world to the
analog world of teh loud speaker.

33
00:02:14,181 --> 00:02:17,726
And the sound card operates with the
system clock of t s.

34
00:02:17,726 --> 00:02:22,508
Now let's assume that our sequence, x of
n, is generated by some algorithm.

35
00:02:22,509 --> 00:02:27,159
By a central processing unit.
On a dedicated audio device, we could use

36
00:02:27,159 --> 00:02:31,995
the same system block to drive both the
production of sample and the output

37
00:02:31,995 --> 00:02:35,108
process.
So the system will be rather simple.

38
00:02:35,108 --> 00:02:39,580
Now on a PC on the other hand, the CPU
works on a clock that is completely

39
00:02:39,580 --> 00:02:44,296
Independent of the sound card clock.
As a matter of fact, there is a clock that

40
00:02:44,296 --> 00:02:48,268
is much, much faster then the clock you
would find in your sound card.

41
00:02:48,268 --> 00:02:52,694
So this situation is the following, the
CPU will generate samples, add its own

42
00:02:52,694 --> 00:02:55,139
pace, and put these samples.
In memory.

43
00:02:55,139 --> 00:03:00,215
The sound card,on the other hand, will
fetch the samples from memory at its own

44
00:03:00,215 --> 00:03:05,324
pace, which is dictated by its clock, and
then output them to the loudspeaker.

45
00:03:05,324 --> 00:03:10,104
The transfer of data from the CPU to
memory happens by a standard memory bus

46
00:03:10,104 --> 00:03:13,788
transaction.
The transfer of data from memory to the

47
00:03:13,788 --> 00:03:17,376
soundcard takes place in the form of a DMA
transfer.

48
00:03:17,376 --> 00:03:22,600
Dma stands for direct memory access.
In other words, the sound card does not

49
00:03:22,600 --> 00:03:26,750
need to invoke the CPU in order to access
the data in memory.

50
00:03:26,750 --> 00:03:31,629
And so this transfer and this transfer.
They can happen asynchronously.

51
00:03:31,629 --> 00:03:36,727
The problem of course, when you have to
asynchronous process is how you have them

52
00:03:36,727 --> 00:03:40,158
communicate.
In particular, how does the sound card

53
00:03:40,158 --> 00:03:45,001
tell the CPU that more data is needed.
This problem is solved by having the CPU

54
00:03:45,001 --> 00:03:50,111
issue a ...an IRQ, an interrupter request,
to the CPU, and when the CPU receives this

55
00:03:50,111 --> 00:03:54,991
signal, it will put another batch of data
in memory for the sound card to access.

56
00:03:54,991 --> 00:03:59,553
Now, if we were to interrupt the CPU every
time we need a new sample, there would be

57
00:03:59,553 --> 00:04:03,987
too much overhead.
So the idea is that the sound card will

58
00:04:03,987 --> 00:04:09,480
consume a chunk of samples already in
memory, called a buffer.

59
00:04:09,480 --> 00:04:14,634
The sound card will notify when the buffer
is completely used up and the CPU will

60
00:04:14,634 --> 00:04:17,427
fill a new buffer for the sound card to
use.

61
00:04:17,427 --> 00:04:22,613
The key point here, since we're talking
about real-time signal processing, is that

62
00:04:22,613 --> 00:04:27,423
the time it takes for the CPU to fill up
the buffer needs to be strictly less than

63
00:04:27,423 --> 00:04:31,095
the time it takes for the sound card to
consume that buffer.

64
00:04:31,096 --> 00:04:41,817
Of course the buffering operation
introduces a delay, but this delay is a

65
00:04:41,817 --> 00:04:48,258
price So by processing the data in
batches.

66
00:04:48,259 --> 00:04:53,456
We insulate data generation process
against concurrent demands on the CPU.

67
00:04:53,456 --> 00:04:58,906
Let's look in more detail how this works.
This is an example of double buffering.

68
00:04:58,906 --> 00:05:04,246
The buffer is this strip here, which is a
set of contiguous location and RAM.

69
00:05:04,246 --> 00:05:10,002
Say L locations for L samples.
The CPU will write newly computed samples

70
00:05:10,002 --> 00:05:16,177
into this buffer and the sound card will
fetch the samples from this buffer for

71
00:05:16,177 --> 00:05:19,952
playing.
There are two pointers into this buffer.

72
00:05:19,952 --> 00:05:25,685
There is a write pointer, used by the cpu
to identifiy the next memory location into

73
00:05:25,685 --> 00:05:29,927
which write a new samples.
And there's a read pointer used by the

74
00:05:29,927 --> 00:05:35,252
sound card, to identify the location from
which the next sample has to be fetched.

75
00:05:35,252 --> 00:05:40,607
The pointers are initialized as such.
We assume that the buffer has been filled

76
00:05:40,607 --> 00:05:45,027
with zeros in the beginning, and we
position the read pointer at the beginning

77
00:05:45,027 --> 00:05:48,787
of the buffer, and the write pointer at
the midpoint of the buffer.

78
00:05:48,787 --> 00:05:52,800
And now the dance begins.
The sound card is started and the sound

79
00:05:52,800 --> 00:05:57,832
card will start fetching samples from the
read buffer and move the read pointer in

80
00:05:57,832 --> 00:06:01,020
this direction.
At the same time the CPU will start

81
00:06:01,020 --> 00:06:05,534
writing samples into the buffer and it
will move the write pointer in this

82
00:06:05,534 --> 00:06:09,465
direction.
Remember the key is that the CPU write

83
00:06:09,465 --> 00:06:13,161
samples faster than the sound card reads
them.

84
00:06:13,161 --> 00:06:18,933
So as time progresses, you can see the
read pointer advances, but the write

85
00:06:18,933 --> 00:06:24,687
pointer advances faster.
And, after a few miliseconds, the CPU will

86
00:06:24,687 --> 00:06:29,391
have filled the second half the buffer and
it will stop.

87
00:06:29,391 --> 00:06:35,653
At the same time, the sound card will keep
reading and when it reads the entirety of

88
00:06:35,653 --> 00:06:41,173
the first half of the buffer It will
signal to the CPU that this part of the

89
00:06:41,173 --> 00:06:47,245
buffer is now depleted before moving on to
reading samples in the 2nd half of the

90
00:06:47,245 --> 00:06:50,735
buffer.
When the CPU receives the IRQ, it knows it

91
00:06:50,735 --> 00:06:54,645
will have to start filling the first half
of the buffer.

92
00:06:54,646 --> 00:06:58,493
This is a circular buffer, as we have seen
in the previous module.

93
00:06:58,493 --> 00:07:03,053
So the pointer will be rolled around, and
the CPU will start right in the samples

94
00:07:03,053 --> 00:07:05,724
here.
So the reading process continues at the

95
00:07:05,724 --> 00:07:10,416
same pace as before, and now the filling
process continues on the first half of the

96
00:07:10,416 --> 00:07:13,325
buffer, at a faster pace than the reading
process.

97
00:07:13,325 --> 00:07:16,996
So again, the CPU will have filled the
first half of the buffer.

98
00:07:16,996 --> 00:07:20,461
While the soundcard is still reading the
second half.

99
00:07:20,461 --> 00:07:25,472
When the second half is depleted, a new RQ
will signal to the CPU that the second

100
00:07:25,472 --> 00:07:29,643
half of the buffer needs to build again.
And so on, and so forth.

101
00:07:29,643 --> 00:07:34,532
Double buffering uses a delay.
That is equal to the system clock times

102
00:07:34,532 --> 00:07:39,605
half the length of the buffer.
If the CPU is not fast enough, the read

103
00:07:39,605 --> 00:07:45,995
pointer of the sound card will trail over
memory cells that contain invalid samples,

104
00:07:45,995 --> 00:07:49,986
old samples.
And we will have a situation of Underflow.

105
00:07:49,986 --> 00:07:54,348
In practical application it's coming to
you as multiple bufferings instead of

106
00:07:54,348 --> 00:07:57,992
simple double buffering.
In this case the buffer is divided into

107
00:07:57,992 --> 00:08:02,481
sub-buffers, and now your cue will be
emitted every time the read pointer trails

108
00:08:02,481 --> 00:08:06,767
one of the boundaries.
The advantages that the CPU will be called

109
00:08:06,767 --> 00:08:11,849
more often, so the load on the CPU will be
better distributed in time while still

110
00:08:11,849 --> 00:08:15,077
affording a reasonable under full
protection.

111
00:08:15,077 --> 00:08:19,134
So, so far we looked at the output process
of the sound card.

112
00:08:19,134 --> 00:08:22,905
What about the input?
The input is really symmetrical.

113
00:08:22,905 --> 00:08:28,338
Instead of having the sound card fetch
sample from memory, we will have the sound

114
00:08:28,338 --> 00:08:33,603
card put samples in memory and notify the
cpu that a new buffer is available for

115
00:08:33,603 --> 00:08:37,368
processing.
The cpu will work at its own pace, it will

116
00:08:37,368 --> 00:08:42,680
share the RAM with the sound card.
The sound card will Both place input

117
00:08:42,680 --> 00:08:47,063
sample in memory.
And fetch out the samples to play.

118
00:08:47,063 --> 00:08:52,597
And will signal to the cpu when either of
these buffers are empty.

119
00:08:52,597 --> 00:08:58,221
Very quickly, the roles are reversed if
this is the buffer in RAM.

120
00:08:58,221 --> 00:09:04,523
We now have that the write pointer belongs
to the sound card, and the read pointer

121
00:09:04,523 --> 00:09:09,039
belongs to the CPU.
We assume as per usual that the initial

122
00:09:09,039 --> 00:09:13,569
buffer is filled with zero.
>> And we start the system.

123
00:09:13,569 --> 00:09:18,840
The CPU will start getting samples from
the buffer, and the sound current will

124
00:09:18,840 --> 00:09:23,915
start writing samples to the buffer.
The CPU will be faster and so it will

125
00:09:23,915 --> 00:09:29,849
deplete the buffer before the sound card
has finished filling the other half of the

126
00:09:29,849 --> 00:09:33,201
buffer.
When the sound card has finished filling

127
00:09:33,201 --> 00:09:38,561
the first half, it will trigger an IRQ and
the CPU will know that this buffer is now

128
00:09:38,561 --> 00:09:42,545
ready for consumption.
So, it will roll the pointer and start

129
00:09:42,545 --> 00:09:46,149
fetching the data and process it.
And so on, and so forth.

130
00:09:46,149 --> 00:09:50,336
We now need to put it all together.
So we will have an input buffer.

131
00:09:50,336 --> 00:09:54,937
And an output buffer.
We will use a multiple buffer strategy,

132
00:09:54,937 --> 00:09:59,570
say, with 3 sub buffers.
We will select the buffer sizes and the

133
00:09:59,570 --> 00:10:04,104
number of sub buffers to be the same both
for input and output.

134
00:10:04,104 --> 00:10:08,104
And we will use the input IRQ to drive the
processing.

135
00:10:08,104 --> 00:10:13,495
This is the input buffer and so the sound
card will place samples into this buffer

136
00:10:13,495 --> 00:10:18,321
as they com from say, the microphone.
This is the output buffer, and so the

137
00:10:18,321 --> 00:10:23,001
sound card will fetch samples from this
buffer and play them through the

138
00:10:23,001 --> 00:10:26,372
loudspeaker.
Assumed that the buffer is a 0 field,

139
00:10:26,372 --> 00:10:31,464
since the clock for the sound card is the
same for both input and output, the input

140
00:10:31,464 --> 00:10:35,944
buffer will be filled at the same rate as
the output buffer is emptied.

141
00:10:35,944 --> 00:10:40,179
After a few milliseconds the first chunk
in the buffer will be filled.

142
00:10:40,179 --> 00:10:44,012
And conversely, the output buffer will be
emptied.

143
00:10:44,012 --> 00:10:49,298
An IRQ will be launched, at which point
the CPU will intervene, and start

144
00:10:49,298 --> 00:10:54,419
processing the input samples, say
filtering them, and placing them.

145
00:10:54,420 --> 00:10:58,470
In the corresponding chunk of the output
buffer for later use.

146
00:10:58,470 --> 00:11:03,624
Again, the CPU will have to perform this
operation faster than the time it takes

147
00:11:03,624 --> 00:11:06,730
for the sound card to fill a chunk of the
buffer.

148
00:11:06,730 --> 00:11:09,671
So here, for instance, when the sound
card.

149
00:11:09,672 --> 00:11:15,044
Has filled say, a third of the next
buffer, the CPU is done with the

150
00:11:15,044 --> 00:11:20,826
processing, It will mark the input
subbuffer as used up, and the output

151
00:11:20,826 --> 00:11:25,016
subbuffer as ready for playing.
The process continues.

152
00:11:25,016 --> 00:11:28,876
The sound card is still filling up the
second chunk of the buffer.

153
00:11:28,876 --> 00:11:32,732
And when it's done, it will launch
naricu/g, and the CPU will start

154
00:11:32,732 --> 00:11:35,911
processing this input chunk into this
output chunk.

155
00:11:35,911 --> 00:11:41,027
The processing requires less time than the
sound card needs to fill the third part of

156
00:11:41,027 --> 00:11:44,673
the, The buffer.
And when the third part of the buffer is

157
00:11:44,673 --> 00:11:50,483
filled, an IRQ will be triggered, pointers
will be rolled around, and the sound card

158
00:11:50,483 --> 00:11:56,218
will start outputting the first chunk of
the buffer that was processed earlier.

159
00:11:56,218 --> 00:12:01,702
While the corresponding chunk of the input
buffer will be filled with new input

160
00:12:01,702 --> 00:12:04,116
samples.
And the process repeats.

161
00:12:04,116 --> 00:12:09,452
Total delay of the system is equivalent to
t s times the number of samples in the

162
00:12:09,452 --> 00:12:12,582
buffer.
Because of how we position the read and

163
00:12:12,582 --> 00:12:16,413
write pointers.
We usually start the output process first,

164
00:12:16,413 --> 00:12:21,201
because it doesn't matter if the input
trails a little bit and buffers can be

165
00:12:21,201 --> 00:12:24,459
collapsed.
In other words, the computation can be

166
00:12:24,459 --> 00:12:25,774
done in place.
Ok.

167
00:12:25,774 --> 00:12:29,417
So, how do we do this in practice?
We get our BC.

168
00:12:29,417 --> 00:12:35,339
And 1 strategy is the low-level strategy.
We study the sound card data sheet, each

169
00:12:35,339 --> 00:12:40,587
chip is different, we write code to
program the sound car directly, this will

170
00:12:40,587 --> 00:12:46,163
require us to write some Particular values
to some particular IO ports, we need to

171
00:12:46,163 --> 00:12:51,740
write an interrupt handler, and we need to
write the code that handles the data.

172
00:12:51,740 --> 00:12:56,380
It's certainly interesting, but very time
consuming, and not very portable.

173
00:12:56,380 --> 00:12:59,006
The high level strategy is to choose a
good.

174
00:12:59,006 --> 00:13:03,992
Api that allows us to attack the
functionality of the sound card without

175
00:13:03,992 --> 00:13:09,138
worrying about the details of it
implementation and just write a callback

176
00:13:09,138 --> 00:13:14,892
function that handles the data when we are
notified that a new Buffer is available.

177
00:13:14,892 --> 00:13:19,343
Let's look at an example of a typical
callback prototype.

178
00:13:19,343 --> 00:13:24,866
We will use C in our next examples.
And this callback will take simply three

179
00:13:24,866 --> 00:13:29,592
mandatory arguments.
A pointer to the input buffer, a pointer

180
00:13:29,592 --> 00:13:33,629
to the output buffer and the length of
said buffers.

181
00:13:33,629 --> 00:13:37,947
So for example, we could write a call back
that simply.

182
00:13:37,948 --> 00:13:43,608
A cast to convert the generic pointers to
the buffers and to the right data type, in

183
00:13:43,608 --> 00:13:48,621
this case, we choose to use floats.
And then, a simple loop that, for each

184
00:13:48,621 --> 00:13:54,088
sample in the input buffer, calls a
processing function and stores the result.

185
00:13:54,088 --> 00:13:58,749
[unknown] Into the output buffer.
So now the question is, what do we put

186
00:13:58,749 --> 00:14:03,338
into the processing function?
So let's expand that and let's define a

187
00:14:03,338 --> 00:14:07,752
generic processing gateway.
If we're going to use filters, we will

188
00:14:07,752 --> 00:14:13,053
need to put in place buffers to store past
samples of the input and of the output.

189
00:14:13,053 --> 00:14:16,323
So, we do this here on this left part of
the panel.

190
00:14:16,323 --> 00:14:19,234
As we said before, we decide to use
floats.

191
00:14:19,235 --> 00:14:24,327
So we defy a buffer of a certain length
and we choose a power of 2 for convenience

192
00:14:24,327 --> 00:14:29,584
so that the circular buffer can be rolled
around with a simple masking operation.

193
00:14:29,584 --> 00:14:34,257
So we define two buffers of equal length
and two indices into the buffer.

194
00:14:34,258 --> 00:14:37,879
The processing function will take the
current sample.

195
00:14:37,879 --> 00:14:40,976
It will store the sample into the input
memory.

196
00:14:40,976 --> 00:14:46,141
It will call a function that we call
effect because we're interested implenting

197
00:14:46,141 --> 00:14:49,769
guitar effects that will compute the
current output.

198
00:14:49,769 --> 00:14:53,935
It will store the output in the memory for
the output samples.

199
00:14:53,935 --> 00:14:59,342
And it will update the indices into the
buffers with the usual circular strategy.

200
00:14:59,342 --> 00:15:04,094
So, we increment the pointer by 1.
And then we roll around the pointer by

201
00:15:04,094 --> 00:15:07,902
binary masking.
And the processing function will return

202
00:15:07,902 --> 00:15:12,416
the current output sample.
That, as you remember, will be put in the

203
00:15:12,416 --> 00:15:17,298
output buffer of the sound card.
So let's look at the simple effects that

204
00:15:17,298 --> 00:15:20,274
we can generate with this programming
paradigm.

205
00:15:20,274 --> 00:15:25,032
An echo is a situation where you have a
sound that bounces back and forth between

206
00:15:25,032 --> 00:15:28,846
2 reflecting surfaces.
And a simple simulation is given by the

207
00:15:28,846 --> 00:15:33,037
following impulse response.
You have the original sound and then you

208
00:15:33,037 --> 00:15:37,335
have a first reflection.
After N samples, and the 2nd reflection

209
00:15:37,335 --> 00:15:41,235
after 2N samples.
So, you have equally spaced replicas of

210
00:15:41,235 --> 00:15:46,500
the signal, which we scale by different
weights in order to simulate a decay in

211
00:15:46,500 --> 00:15:49,555
time.
So, we have the original signal scaled by

212
00:15:49,555 --> 00:15:54,928
A, A is usually 1, a 1st reflection scaled
by B And the 2nd reflection[UNKNOWN] by C.

213
00:15:54,928 --> 00:15:58,964
And then, we normalize the sum of the
reflection by the sum of the coefficients

214
00:15:58,964 --> 00:16:02,516
in order to have unit gain.
To implement this in our framework, all we

215
00:16:02,516 --> 00:16:06,494
need is a straight forward translation of
the transfer function into C code.

216
00:16:06,494 --> 00:16:11,736
So here, we define the 3 coefficients that
we used for the 3 Replicas of the signal,

217
00:16:11,736 --> 00:16:17,357
the normalizing coefficient, and the delay
between replicas, which is here expressed

218
00:16:17,357 --> 00:16:20,778
as a function, of course, of the sample of
frequency.

219
00:16:20,778 --> 00:16:25,508
The output value is simply the wave sum of
three delayed replicas of.

220
00:16:25,508 --> 00:16:31,355
The input.
Acoustically a simple echo sounds like

221
00:16:31,355 --> 00:16:36,671
this.
First you will hear the original sound.

222
00:16:36,671 --> 00:16:44,336
And then you will hear the same sound
processed by the simple echo.

223
00:16:44,336 --> 00:16:48,965
[music] The simple echo has two drawbacks
with respect to a natural echo.

224
00:16:48,965 --> 00:16:53,525
The first one is that it has only a finite
number of repetition, whereas in a natural

225
00:16:53,525 --> 00:16:57,749
situation it would have a theoretically
infinite number of back-and-forth

226
00:16:57,749 --> 00:17:00,348
reflections.
The second drawback is that each

227
00:17:00,348 --> 00:17:04,212
repetition is just...
(End of transcription.) An exact replica

228
00:17:04,212 --> 00:17:07,332
of the original sound simply scaled in
amplitude.

229
00:17:07,332 --> 00:17:12,063
Whereas the reflection process in a
natural echo would introduce a low pass

230
00:17:12,063 --> 00:17:16,926
characteristic in each reflection.
To obtain a better echo we can go back to

231
00:17:16,926 --> 00:17:19,805
our old friend the carpal strong
algorithm.

232
00:17:19,805 --> 00:17:25,055
If you remember the carpal strong works by
building equally spaced replicas of the

233
00:17:25,055 --> 00:17:27,986
input signal.
To produce the output.

234
00:17:27,986 --> 00:17:33,752
The fact that the input signal is final
support prevents overlap between the

235
00:17:33,752 --> 00:17:39,692
output copies, but if we input a standard
sequence to the feedback loop, we will

236
00:17:39,692 --> 00:17:44,557
have overlap and[UNKNOWN] Time.
So the feedback loop already takes care of

237
00:17:44,557 --> 00:17:48,081
the fact you want an infinite number of
repetitions in the echo.

238
00:17:48,081 --> 00:17:51,922
In order to introduce the low pass
characteristic of a natural echo, we

239
00:17:51,922 --> 00:17:55,313
simply introduce a low pass filter.
In the feedback path.

240
00:17:55,313 --> 00:18:00,119
And the output at this point, can be
expressed as the usual decay factor times

241
00:18:00,119 --> 00:18:05,069
the convolution of the output, and the
input's response of the low pass delayed

242
00:18:05,069 --> 00:18:09,268
by capital M plus the input.
If we choose a simple low pass such as the

243
00:18:09,268 --> 00:18:14,152
leaky integrator, we can actually write
the constant coefficient difference

244
00:18:14,152 --> 00:18:19,311
equation, which turns out to be like that.
And if we compute the impulse response

245
00:18:19,311 --> 00:18:24,547
numerically, we get something that Each
repetition of the period of the echo gets

246
00:18:24,547 --> 00:18:29,552
smoother and smoother, which is really
what you obtain if you apply a low-pass

247
00:18:29,552 --> 00:18:33,511
filter repeatedly.
The implementation in our processing

248
00:18:33,511 --> 00:18:36,818
framework is, once again, very straight
forward.

249
00:18:36,818 --> 00:18:41,752
We just need to convert the constant
coefficient difference equation to see

250
00:18:41,752 --> 00:18:45,145
statements.
We define the decay factor, the lambda

251
00:18:45,145 --> 00:18:50,439
factor for the leaky integrator.
We define The normalizing factor that will

252
00:18:50,439 --> 00:18:55,709
preserve unit gain, and again, the echo
delay as a function of the sampling

253
00:18:55,709 --> 00:18:59,379
frequency.
And then we return the output as a linear

254
00:18:59,379 --> 00:19:04,929
combination of input and output samples.
If we now play the same clip as before

255
00:19:04,929 --> 00:19:12,358
through this natural echo.
It will hopefully sound better and more

256
00:19:12,358 --> 00:19:19,458
realistic.
So here is a comparison between the two.

257
00:19:20,870 --> 00:19:26,934
The next linear time and variant effect
that we will consider is reverb.

258
00:19:26,934 --> 00:19:33,450
Reverb is really the superimposition of
very, very many echolike reflections that

259
00:19:33,450 --> 00:19:39,168
come from different directions and
therefore have different delays...

260
00:19:39,168 --> 00:19:44,728
(End of transcription.) And different type
of attenuation is associated to them.

261
00:19:44,728 --> 00:19:49,698
Because of the complexity of the impulse
response associated to reverb, precise

262
00:19:49,698 --> 00:19:53,583
simulations are very costly in terms of
computational power.

263
00:19:53,583 --> 00:19:58,177
But if we want to just approximate the
reverb as would be generated by a very

264
00:19:58,177 --> 00:20:03,061
small environment with highly reflective
surfaces You can get a way, by using a

265
00:20:03,061 --> 00:20:07,707
simple allpass filter as shown here.
Remember the allpass filter is a filter

266
00:20:07,707 --> 00:20:12,376
whose magnitude response is unit over the
entire minus pi to pi frequency axis.

267
00:20:12,376 --> 00:20:17,212
By appropriately choosing the parameters
alpha and capital N in the allpass, we can

268
00:20:17,212 --> 00:20:21,936
obtain a phase response with the staircase
like characteristics shown here.

269
00:20:21,936 --> 00:20:26,602
This phase response will introduce
different delays to different frequency

270
00:20:26,602 --> 00:20:29,413
regions.
Thereby simulating a multi-faceted

271
00:20:29,413 --> 00:20:33,186
reflective surface.
The implementation of the all pass filter

272
00:20:33,186 --> 00:20:37,828
is again extremely straightforward.
We only have two parameters to choose.

273
00:20:37,828 --> 00:20:42,488
And then we just implement the cutsom
coefficient difference equation in c.

274
00:20:42,489 --> 00:20:50,736
We can now try to apply the reverb to a
little guitar snippet.

275
00:20:50,736 --> 00:20:58,202
We'll play the original and then the
processed signal.

276
00:20:59,208 --> 00:21:04,036
Let's now look at some very common guitar
effects that do not belong to the class of

277
00:21:04,036 --> 00:21:08,053
linear time invariant filters.
This is to give you an idea that there is

278
00:21:08,053 --> 00:21:12,440
a whole world out there beyond linear
processing that is very, very interesting.

279
00:21:12,440 --> 00:21:15,833
The 1st type of effect that we will
consider is the distortion.

280
00:21:15,833 --> 00:21:19,573
Simple distortion can be achieved by
truncating the scaled version.

281
00:21:19,573 --> 00:21:24,754
Of the input signal and bringing the
signal back to the original amplitude.

282
00:21:24,754 --> 00:21:29,640
Tremelo is an effect achieved by
multiplying the input signal by a slowly

283
00:21:29,640 --> 00:21:34,708
varying sinusoidal component.
This will change the volume periodically

284
00:21:34,708 --> 00:21:38,160
in time.
Flanger is obtained by summing a signal to

285
00:21:38,160 --> 00:21:42,402
a delayed version of itself and by varying
the delay in time.

286
00:21:42,403 --> 00:21:45,556
Usually according to a sinusoidal pattern
as well.

287
00:21:45,556 --> 00:21:50,392
When you sum a signal to a delayed version
of itself, you obtain some spectral nulls

288
00:21:50,392 --> 00:21:53,737
and frequency.
By changing the delay, the spectrum nulls

289
00:21:53,737 --> 00:21:57,824
will change their position in time, and
this will give rise to a variety of

290
00:21:57,824 --> 00:22:01,217
effects, from say, a robotic sound to an
artificial chorus.

291
00:22:01,217 --> 00:22:05,773
From the point of view of implementation,
the fact that these effects are nonlinear

292
00:22:05,773 --> 00:22:08,538
doesn't really complexify the associated
code.

293
00:22:08,538 --> 00:22:12,609
And this is really one of the strong
points of digital signal processing.

294
00:22:12,609 --> 00:22:17,100
We can easily move beyond the standard
filtering paradigm without incurring much

295
00:22:17,100 --> 00:22:23,167
complexity.
So distortion simply takes the input

296
00:22:23,167 --> 00:22:30,976
signal, operates a hard limit, and
rescales the output.

297
00:22:30,976 --> 00:22:38,292
And it sounds like this.
[music] Tremolo requires you to define the

298
00:22:38,292 --> 00:22:45,002
speed of the modulated sinusoid and then
the output is obtained simply by

299
00:22:45,002 --> 00:22:50,954
multiplying the input times an
appropriately scaled sinusoid.

300
00:22:50,954 --> 00:22:58,465
Tremolo sounds like this.
Finally, with flanger, we need to define

301
00:22:58,465 --> 00:23:05,955
the maximum delay, the frequency at which
this delay will oscillate around its mean

302
00:23:05,955 --> 00:23:10,813
value, and then we compute the
instantaneous delay.

303
00:23:10,814 --> 00:23:21,902
By converting to an integer, the product
of the maximum delay times the oscillating

304
00:23:21,902 --> 00:23:28,827
component.
And with some, the two delayed versions of

305
00:23:28,827 --> 00:23:31,495
the input.
[music].
