Skip to content

Commit 5ccd4a5

Browse files
authored
Merge pull request #1890 from annietllnd/content-review
Technical review of CMSIS-DSP LP
2 parents 00c44ae + 1448138 commit 5ccd4a5

File tree

9 files changed

+372
-409
lines changed

9 files changed

+372
-409
lines changed

content/learning-paths/embedded-and-microcontrollers/cmsisdsp-dev-with-python/_index.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ title: Getting Started with CMSIS-DSP Using Python
33

44
minutes_to_complete: 30
55

6-
who_is_this_for: Developers writing DSP/AI software
6+
draft: true
7+
cascade:
8+
draft: true
79

8-
learning_objectives:
10+
who_is_this_for: Developers who want to learn how the CMSIS-DSP package can be integrated into their applications
11+
12+
learning_objectives:
913
- Understand how to use the CMSIS-DSP Python package
1014
- Understand how the Python implementation maps to the C implementation
1115
- Develop a complex application using CMSIS-DSP
1216

1317
prerequisites:
14-
- Some familiarity with DSP programming
15-
- Some familiarity with Python programming
18+
- Some familiarity with Python and DSP programming
1619
- Knowledge of C
1720
- Some familiarity with CMSIS-DSP
1821
- Python installed on your system
@@ -26,20 +29,16 @@ armips:
2629
- Cortex-M
2730
- Cortex-A
2831
tools_software_languages:
29-
- VS Code
3032
- CMSIS-DSP
3133
- Python
3234
- C
3335
- Jupyter Notebook
36+
- numpy
3437
operatingsystems:
3538
- Linux
3639
- Windows
3740
- macOS
3841

39-
40-
41-
42-
4342
further_reading:
4443
- resource:
4544
title: Biquad filters with CMSIS-DSP Python package
@@ -61,7 +60,7 @@ further_reading:
6160
title: CMSIS-Stream
6261
link: https://github.com/ARM-software/CMSIS-Stream
6362
type: Open-source project
64-
63+
6564

6665
### FIXED, DO NOT MODIFY
6766
# ================================================================================

content/learning-paths/embedded-and-microcontrollers/cmsisdsp-dev-with-python/how-to-1.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
---
2-
title: What is the CMSIS-DSP Python package ?
2+
title: CMSIS-DSP Python package
33
weight: 2
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
## What is CMSIS-DSP ?
9+
## What is CMSIS-DSP?
1010

1111
CMSIS-DSP is a general-purpose compute library with a focus on DSP. It was initially developed for Cortex-M processors and has recently been upgraded to also support Cortex-A.
1212

1313
On each processor, CMSIS-DSP is optimized for the architecture: DSP extensions on M4 and M7; Helium on M55 and M85; Neon on A55, etc.
1414

15-
## What is the CMSIS-DSP Python package ?
15+
## What is the CMSIS-DSP Python package?
1616

1717
The CMSIS-DSP Python package is a Python API for CMSIS-DSP. Its goal is to make it easier to develop a C solution using CMSIS-DSP by decreasing the gap between a design environment like Python and the final C implementation.
1818

content/learning-paths/embedded-and-microcontrollers/cmsisdsp-dev-with-python/how-to-2.md

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ weight: 3
66
layout: learningpathall
77
---
88

9-
## Installing the Python packages
10-
The application you will develop with CMSIS-DSP requires a few additional Python packages besides CMSIS-DSP. These need to be installed before you start writing code.
9+
The application you will develop requires a few additional Python packages besides CMSIS-DSP. These need to be installed before you start writing code.
1110

12-
Activate the Python environment you have chosen.
11+
You should install the packages in a Python virtual environment. For example, you can use:
12+
13+
```
14+
python -m venv cmsis-dsp-venv
15+
```
1316

1417
The first package to install is CMSIS-DSP:
1518

@@ -18,43 +21,32 @@ pip install cmsisdsp
1821
```
1922
It will also install `NumPy`, which is a dependency of the CMSIS-DSP Python package.
2023

21-
You'll be working with a Jupyter notebook, so the jupyter package must also be installed:
24+
You'll be working with a Jupyter notebook, so the `jupyter` package must also be installed:
2225

2326
```bash
2427
pip install jupyter
2528
```
2629

27-
In the Jupyter notebook, you'll be using widgets to play sound, so you'll need to install some additional Jupyter widgets.
28-
29-
```bash
30-
pip install ipywidgets
31-
```
32-
33-
Finally, you'll need packages to read sound files and display plots:
34-
30+
Finally, you'll need packages to read sound files, play sound using widgets, and display plots:
3531

3632
```bash
37-
pip install soundfile
38-
pip install matplotlib
33+
pip install soundfile ipywidgets matplotlib
3934
```
4035

41-
you can now launch the Jupyter notebook:
36+
You can now launch the Jupyter notebook with the following command:
4237

4338
```bash
4439
jupyter notebook
4540
```
46-
Create a new Jupyter notebook by clicking `new` and selecting `Python 3 (ipykernel)`.
47-
48-
The new notebook will be named `Untitled`. Rename it to something more descriptive.
49-
50-
You can now import all the required packages.
41+
A browser window should open showing the source tree your terminal launched from. Create a new Jupyter notebook by clicking the `New` dropdown and selecting `Python 3 (ipykernel)`. The new notebook will be named `Untitled`. Rename it to something more descriptive, for example `cmsis-dsp`.
5142

52-
Type the following Python code into your notebook and run the cell (shift-enter).
53-
All the Python code in this learning path is intended to be executed in the same Jupyter notebook.
43+
You can now import all the required packages. Copy the following Python code into your notebook and run the cell (Shift+Enter).
44+
All the Python code blocks in this learning path are intended to be executed in the same Jupyter notebook.
5445

5546
```python
56-
import cmsisdsp as dsp
47+
import cmsisdsp as dsp
5748
import cmsisdsp.fixedpoint as fix
49+
from cmsisdsp import datatype as dt
5850
import numpy as np
5951
from numpy.lib.stride_tricks import sliding_window_view
6052

@@ -69,8 +61,8 @@ from IPython.display import display,Audio
6961
import io
7062
import soundfile as sf
7163

72-
# To load test patterns from the Arm Virtual Hardware Echo Canceller dem
64+
# To load test patterns from the Arm Virtual Hardware Echo Canceller demo
7365
from urllib.request import urlopen
7466
```
7567

76-
You're now ready to move on to the next steps.
68+
You're now ready to move on to the next steps, where you will set up some audio files for processing.

content/learning-paths/embedded-and-microcontrollers/cmsisdsp-dev-with-python/how-to-3.md

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ layout: learningpathall
88

99
## Load an audio file
1010

11-
Load an audio file from one of the Arm demo repositories on GitHub.
12-
11+
You will use an audio file from one of the Arm demo repositories on GitHub.
1312

1413
```python
1514
test_pattern_url="https://github.com/ARM-software/VHT-SystemModeling/blob/main/EchoCanceller/sounds/yesno.wav?raw=true"
@@ -18,20 +17,17 @@ filedata = f.read()
1817
```
1918

2019
You can now play and listen to the audio:
20+
2121
```python
2222
audio=Audio(data=filedata,autoplay=False)
2323
audio
2424
```
2525

26-
An audio widget will appear in your Jupyter notebook. It will look like this:
26+
An audio widget will appear in your Jupyter notebook:
2727

2828
![audio widget alt-text#center](audiowidget.png "Figure 1. Audio widget")
2929

30-
You can use it to listen to the audio.
31-
32-
You'll hear a sequence of the words "yes" and "no", with some noise between them.
33-
The goal of this learning path is to design an algorithm to remove the noise.
34-
30+
You can use it to listen to the audio. You'll hear a sequence of the words "yes" and "no", with some noise between them. The goal of this learning path is to design an algorithm to remove the noise.
3531

3632
Next, convert the audio into a NumPy array so that it can be processed using CMSIS-DSP:
3733

@@ -65,9 +61,7 @@ In the picture, you can see a sequence of words. Between the words, the signal i
6561

6662
In a real application, you don't wait for the entire signal to be received. The signal is continuous. The samples are processed as they are received. Processing can either be sample-based or block-based. For this learning path, the processing will be block-based.
6763

68-
Before you can move to the next step, this signal must be split into blocks. The processing will occur on small blocks of samples of a given duration.
69-
70-
64+
Before you can move to the next step, this signal must be split into blocks. The processing will occur on small blocks of samples of a given duration, known as windows.
7165

7266
```python
7367
winDuration=30e-3/6
@@ -79,9 +73,7 @@ slices=sliding_window_view(data,winLength)[::winOverlap,:]
7973
slices_q15=sliding_window_view(dataQ15,winLength)[::winOverlap,:]
8074
```
8175

82-
Refer to the [NumPy documentation](https://numpy.org/doc/stable/reference/generated/numpy.lib.stride_tricks.sliding_window_view.html) for details about `sliding_window_view`. It's not the most efficient function, but it is sufficient for this tutorial.
83-
84-
The signal is split into overlapping blocks: each block reuses half of the samples from the previous block as defined by the `winOverlap` variable.
76+
Refer to the [NumPy documentation](https://numpy.org/doc/stable/reference/generated/numpy.lib.stride_tricks.sliding_window_view.html) for details about `sliding_window_view`. It's not the most efficient function, but it is sufficient for this tutorial. The signal is split into overlapping blocks: each block reuses half of the samples from the previous block as defined by the `winOverlap` variable.
8577

86-
You are now ready to move on to the next step: you have an audio signal that has been split into overlapping blocks, and processing will occur on those blocks.
78+
By running that last block, you have an audio signal that has been split into overlapping blocks. The next step is to do some processing on those blocks.
8779

content/learning-paths/embedded-and-microcontrollers/cmsisdsp-dev-with-python/how-to-4.md

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,11 @@ layout: learningpathall
88

99
## Write a simple voice activity detection
1010

11-
To remove the noise between speech segments, you need to detect when voice is present.
11+
To remove the noise between speech segments, you need to detect when voice is present. Voice activity detection (VAD) can be complex, but for this learning path, you'll implement a very simple and naive approach based on _energy_. The idea is that if the environment isn't too noisy, speech should have more energy than the noise.
1212

13-
Voice activity detection can be complex, but for this learning path, you'll implement a very simple and naive approach based on energy. The idea is that if the environment isn't too noisy, speech should have more energy than the noise.
13+
The detection will rely on a comparison with a threshold that must be manually tuned. That threshold will be hard-coded, which might not work in a real-life use-case, but is a sufficient solution for this learning path. You'll first implement a version of the VAD with NumPy, which will serve as a reference. Then you'll implement the same version using CMSIS-DSP with the Q15 fixed-point format.
1414

15-
The detection will rely on a comparison with a threshold that must be manually tuned.
16-
17-
You'll first implement a version of the voice activity detection (VAD) with NumPy, which will serve as a reference.
18-
19-
Then you'll implement the same version using CMSIS-DSP with the Q15 fixed-point format.
15+
Throughout this section, you should copy the code-blocks and run them in your Jupyter notebook.
2016

2117
### NumPy VAD
2218

@@ -38,9 +34,7 @@ def signal_vad(window):
3834
return(0)
3935
```
4036

41-
The threshold is hard-coded. It's not a very clean solution, but it's sufficient for a tutorial.
42-
43-
When using such a detector, you'll quickly find that it is not sufficient. You'll need another pass to clean up the detection signal.
37+
Additionally, you'll need another pass to clean up the detection signal.
4438

4539
```python
4640
def clean_vad(v):
@@ -53,7 +47,7 @@ def clean_vad(v):
5347
return(vmax)
5448
```
5549

56-
Now you can apply this algorithm to the audio signal and plot the VAD detection over it to see if it's working:
50+
Now you can apply this algorithm to the audio signal and plot the VAD over it to see if it's working:
5751

5852
```python
5953
_,ax=plt.subplots(1,1)
@@ -62,58 +56,58 @@ vad = np.array([[w]*(winLength-winOverlap) for w in cleaned]).flatten()
6256
ax.plot(data)
6357
ax.plot(vad)
6458
```
65-
![vad alt-text#center](vad.png "Figure 3. VAD")
59+
![vad alt-text#center](vad.png "Figure 3. VAD reference implementation")
6660

67-
The reference implementation works. You can now implement the same version using CMSIS-DSP.
61+
This plot shows you that the reference implementation works. The next step is to implement a similar graph using CMSIS-DSP.
6862

6963
### CMSIS-DSP Q15 VAD
7064

71-
First, you need to compute the signal energy from audio in Q15 format using CMSIS-DSP.
65+
#### Energy
66+
First, you need to compute the signal energy from audio in Q15 format using CMSIS-DSP.
7267

73-
If you look at the CMSIS-DSP documentation, you'll see that the power and log functions don't produce results in Q15 format. Tracking the fixed-point format throughout all lines of an algorithm can be challenging.
68+
If you look at the CMSIS-DSP documentation, you'll see that the `power` and `vlog` functions don't produce results in Q15 format. Tracking the fixed-point format throughout all lines of an algorithm can be challenging. In this example, this means that:
69+
70+
* Subtracting the mean to center the signal - as you did in the reference implementation - is handled in CMSIS-DSP by negating the mean and applying it as an offset to the window. Because the mean is small and the shift is minor relative to the Q15 range, this adjustment won't cause saturation.
71+
* The resulting `energy` and `dB` values are not in Q15 format because the `power` and `vlog` functions are used
72+
* The multiplication by 10 from the reference implementation is missing
73+
74+
This means that the `signal_energy_q15` will have a different output than the above implementation. Instead of trying to determine the exact fixed-point format of the output and applying the necessary shift to adjust the output's fixed-point format, you will address it in the next step. By tuning the threshold of the detection function, the comparison with the reference VAD can be valid.
7475

75-
For this tutorial, instead of trying to determine the exact fixed-point format of the output and applying the necessary shift to adjust the output's fixed-point format, we'll simply tune the threshold of the detection function.
7676

7777
```python
7878
def signal_energy_q15(window):
79+
# Calculate window
7980
mean=dsp.arm_mean_q15(window)
80-
# Subtracting the mean won't cause saturation
81-
# So we use the CMSIS-DSP negate function on an array containing a single sample.
8281
neg_mean=dsp.arm_negate_q15([mean])[0]
8382
window=dsp.arm_offset_q15(window,neg_mean)
83+
84+
# Energy of the window
8485
energy=dsp.arm_power_q15(window)
85-
# Energy is not in Q15 format (refer to the CMSIS-DSP documentation).
8686
energy=dsp.ssat(energy>>20,16)
8787
dB=dsp.arm_vlog_q15([energy])
88-
# The output of the `vlog` is not in q15
89-
# The multiplication by 10 is missing compared to the NumPy
90-
# reference implementation.
91-
# The result of this function is not equivalent to the float implementation due to different
92-
# formats used in intermediate computations.
93-
# As a consequence, a different threshold must be used to compensate for these differences.
88+
9489
return(dB[0])
9590
```
9691

92+
#### VAD
93+
9794
The comparison function is very similar to the NumPy reference, but the threshold is different:
9895

9996
```python
10097
def signal_vad_q15(window):
101-
# The threshold is not directly comparable to the float implementation
102-
# due to the different intermediate formats used in the fixed-point implementation.
10398
if signal_energy_q15(window)>fix.toQ15(-0.38):
10499
return(1)
105100
else:
106101
return(0)
107102
```
108103

109-
Note that in a C code, you would use the output of `fix.toQ15(-0.38)`.
110-
111-
`fix.toQ15` is a utility of the Python package to convert float to fixed-point. It is not available in the CMSIS-DSP C implementation.
112-
CMSIS-DSP C has functions like `arm_float_to_q15` which work on arrays and are meant to be used at runtime. If you need a precomputed constant, you can use a utility function like `fix.toQ15` and use the resulting value in the code.
104+
{{% notice Note %}}
105+
In C code, you would hard-code the output of `fix.toQ15(-0.38)`. `fix.toQ15` is a utility of the Python package to convert float to fixed-point, but it is not available in the CMSIS-DSP C implementation. CMSIS-DSP C has functions like `arm_float_to_q15` which work on arrays and are meant to be used at runtime. If you need a pre-computed constant, you can use a utility function like `fix.toQ15` during development and use the resulting value in the C code.
106+
{{% /notice %}}
113107

114-
The clean VAD function is the same for both the NumPy and Q15 versions.
108+
#### Plot the Q15 implementation
115109

116-
Now you can check whether the Q15 version is working by plotting the signal and the output of the Q15 VAD algorithm.
110+
The clean VAD function is now the same for both the NumPy and Q15 versions. You can check whether the Q15 version is working by plotting the signal and the output of the Q15 VAD algorithm.
117111

118112
```python
119113
_,ax=plt.subplots(1,1)
@@ -122,4 +116,6 @@ vad_q15 = np.array([[w]*winOverlap for w in cleaned]).flatten()
122116
ax.plot(data)
123117
ax.plot(vad_q15)
124118

125-
```
119+
```
120+
121+
Now that you have a working VAD implementation, move on to the next section to combine the principles into a Python class for noise suppression.

0 commit comments

Comments
 (0)