Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Acoustic event detector #46

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions software/apps/audio_module/event_detector/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# makefile for user application

# the current directory
APP_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

# files needed for this code
C_SRCS := $(wildcard *.c)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the Makefile variable you want to adjust if you want to have a non-flat hierarchy. I believe something like this would include all C files in all subdirectories work:

C_SRCS := $(wildcard *.c)
C_SRCS += $(wildcard **/*.c)

You could also be more explicit if you only want to include certain subdirectories or files.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Amit,
Thank you so much for your help! I tried

C_SRCS   := $(wildcard *.c)
C_SRCS   := $(wildcard **/log2fix.c)

but still get

DEP        log2fix/log2fix.c
<built-in>: fatal error: opening dependency file /mnt/c/Users/Long/Projects/signpost/software/apps/audio_module/event_detector/build/cortex-m4/log2fix/log2fix.d: No such file or directory
compilation terminated.

Namely, the Makefile still looks under the build/ but didn't copy the source files into it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh... yes you are right... the problem is that there is no lib2fix subdirectory in the build/cortex-m4 directory and our Makefile rule assumes there is (and GCC won't just create one). If you create one after the build fails the first time, it will be able to move past it, but that's not solution.

@ppannuto please halp! You are the resident dependency file expert!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick follow-up on this motivated by today's phone call. This should be largely fixed by the updates to the Tock build system. I updated signpost a few days ago in the tock-master branch, but we're holding off on merging that until after the paper deadline. I'll follow up again on April 11 :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Pat,
Thanks for the follow-up. And no worries at all, I understand the burden of a paper deadline. We'll get this working soon.
In the meantime, godspeed to your paper!

C_SRCS := $(wildcard **/log2fix.c)
#C_SRCS += ./log2fix/log2fix.c
#C_SRCS += ./kiss_fft/kiss_fft.c
#C_SRCS += ./kiss_fft/tools/kiss_fftr.c
#C_SRCS += ./gcwa/c/ridgeTracker.c
#C_SRCS += ./gcwa/c/dynArray.c

INCLUDE_PATHS += . ./kiss_fft ./kiss_fft/tools ./log2fix ./gcwa/c

CFLAGS += -DFIXED_POINT=16 -std=c99
CFLAGS += -Wno-type-limits -Wno-sign-compare

TOCK_BOARD = audio_module

# include makefile settings that are shared between applications
include ../../AppMakefile.mk

25 changes: 25 additions & 0 deletions software/apps/audio_module/event_detector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Universal acoustic event detection app
The first step into any audio processing chain.

Detect a large class of "acoustic events" (bird calls, gunshots, broken glasses, etc.) from noisy background.
Further classifications are reserved for downstream processing.

## Quick start
1. Plug the Audio Module into the `Module 1` slot

2. **Important** Make sure the programming knob is turned to `MOD1`.

3. Get all the prerequisites (See below)

4. Flash the app

```bash
cd signpost/software/apps/audio_module/event_detector
make flash
```

## Prerequisites
* Fixed-point FFT: git clone https://github.com/longle2718/kiss_fft
* Fixed-point log: git clone https://github.com/dmoulding/log2fix
* Ridge tracker: git clone https://bitbucket.org/longle1/gcwa
* Fixed-point implementation in C is under gcwa/c/
161 changes: 161 additions & 0 deletions software/apps/audio_module/event_detector/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>

#include <adc.h>
#include <console.h>
#include <gpio.h>
#include <led.h>
#include <timer.h>
#include <tock.h>

#include "app_watchdog.h"
#include "signpost_api.h"
#include "simple_post.h"
#include "_kiss_fft_guts.h"
#include "kiss_fftr.h"
#include "common.h"
#include "ridgeTracker.h"

static int freqAnalyze(kiss_fft_scalar *frame, kiss_fft_scalar *spec);
static int printMat(char *filename, kiss_fft_scalar *in, size_t N);
static int printMat2(char *filename, size_t *in, size_t N);

int main (void) {
int err = SUCCESS;
printf("[Audio Module] Event Detector\n");

// initialize ADC
err = adc_initialize();
if (err < SUCCESS) {
printf("Initialize errored: %d\n", err);
}

static kiss_fft_scalar frame[BUF_LEN];
static kiss_fft_scalar spec[FRE_LEN];
static kiss_fft_scalar snrOut[FRE_LEN];
memset(frame, 0, BUF_LEN*sizeof(kiss_fft_scalar));
size_t frameIdx = 0;
size_t globTI = 0;
ridgeTracker_init();
while (true) {
// read data from ADC
for (size_t k = frameIdx; k < frameIdx+INC_LEN; k++){
err = adc_read_single_sample(3);
if (err < SUCCESS) {
printf("ADC read error: %d\n", err);
}
uint16_t sample = err & 0xFFFF;
frame[k] = (kiss_fft_scalar)sample;
}
frameIdx = (frameIdx+INC_LEN) % BUF_LEN;

// perform FFT
if (freqAnalyze(frame, spec)){
printf("freqAnalyze() failed!\n");
return 1;
}

// ridgetracker update
ridgeTracker_update(spec, snrOut);

// check for available event
if (ridgeTracker_isReady){
printf("Event detected!\n");
printf("ridgeTracker_out.used = %zu\n",ridgeTracker_out.used);

if (printMat("SNR.mat",ridgeTracker_out.SNR,ridgeTracker_out.used)){
printf("printMat() failed!\n");
return 1;
}
if (printMat2("FI.mat",ridgeTracker_out.FI,ridgeTracker_out.used)){
printf("printMat2() failed!\n");
return 1;
}
size_t TIE = ridgeTracker_out.TI[ridgeTracker_out.used-1];
for (size_t k=0; k<ridgeTracker_out.used; k++){
ridgeTracker_out.TI[k] = globTI-(TIE-ridgeTracker_out.TI[k]);
}
if (printMat2("TI.mat",ridgeTracker_out.TI,ridgeTracker_out.used)){
printf("printMat2() failed!\n");
return 1;
}

ridgeTracker_reset();
}
globTI += 1;
}

ridgeTracker_destroy();

printf("Done!\n");
fflush(stdout);
return 0;
}

static int freqAnalyze(kiss_fft_scalar *frame, kiss_fft_scalar *spec){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, these static helper methods are returning positive integers on error, which is pretty unintuitive for C. I think it would be better to return negative numbers (common for errors) and check if(helper_function() < 0) in main.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thank you very much for telling me about the correct style in C. I'll fix this accordingly.

// FFT data structure
kiss_fftr_cfg cfg;
kiss_fft_scalar in[BUF_LEN];
kiss_fft_cpx out[FRE_LEN + 1];

static kiss_fft_scalar win[BUF_LEN] = {0x0000,0x0001,0x0004,0x000b,0x0013,0x001e,0x002c,0x003c,0x004f,0x0064,0x007b,0x0095,0x00b2,0x00d0,0x00f2,0x0115,0x013c,0x0164,0x018f,0x01bd,0x01ec,0x021f,0x0253,0x028a,0x02c4,0x0300,0x033e,0x037e,0x03c1,0x0406,0x044e,0x0497,0x04e3,0x0532,0x0583,0x05d5,0x062b,0x0682,0x06dc,0x0737,0x0796,0x07f6,0x0858,0x08bd,0x0923,0x098c,0x09f7,0x0a64,0x0ad3,0x0b44,0x0bb8,0x0c2d,0x0ca4,0x0d1d,0x0d98,0x0e15,0x0e94,0x0f15,0x0f98,0x101d,0x10a4,0x112c,0x11b6,0x1242,0x12d0,0x1360,0x13f1,0x1484,0x1519,0x15af,0x1647,0x16e0,0x177c,0x1818,0x18b7,0x1956,0x19f8,0x1a9a,0x1b3f,0x1be4,0x1c8b,0x1d34,0x1ddd,0x1e88,0x1f35,0x1fe2,0x2091,0x2141,0x21f3,0x22a5,0x2359,0x240d,0x24c3,0x257a,0x2632,0x26eb,0x27a5,0x285f,0x291b,0x29d8,0x2a95,0x2b53,0x2c12,0x2cd2,0x2d93,0x2e54,0x2f16,0x2fd9,0x309c,0x3160,0x3224,0x32e9,0x33ae,0x3474,0x353b,0x3601,0x36c9,0x3790,0x3858,0x3920,0x39e9,0x3ab1,0x3b7a,0x3c43,0x3d0c,0x3dd6,0x3e9f,0x3f68,0x4032,0x40fb,0x41c5,0x428e,0x4357,0x4420,0x44e9,0x45b2,0x467b,0x4743,0x480b,0x48d3,0x499a,0x4a61,0x4b28,0x4bee,0x4cb3,0x4d79,0x4e3d,0x4f01,0x4fc5,0x5088,0x514a,0x520c,0x52cd,0x538d,0x544c,0x550b,0x55c9,0x5686,0x5742,0x57fd,0x58b8,0x5971,0x5a29,0x5ae1,0x5b97,0x5c4c,0x5d00,0x5db3,0x5e65,0x5f16,0x5fc5,0x6074,0x6121,0x61cc,0x6277,0x6320,0x63c7,0x646e,0x6513,0x65b6,0x6658,0x66f9,0x6798,0x6835,0x68d1,0x696c,0x6a04,0x6a9c,0x6b31,0x6bc5,0x6c57,0x6ce7,0x6d76,0x6e03,0x6e8e,0x6f17,0x6f9f,0x7025,0x70a8,0x712a,0x71aa,0x7228,0x72a5,0x731f,0x7397,0x740d,0x7481,0x74f3,0x7564,0x75d2,0x763e,0x76a7,0x770f,0x7775,0x77d8,0x783a,0x7899,0x78f6,0x7950,0x79a9,0x79ff,0x7a53,0x7aa5,0x7af5,0x7b42,0x7b8d,0x7bd5,0x7c1c,0x7c60,0x7ca1,0x7ce1,0x7d1e,0x7d58,0x7d90,0x7dc6,0x7dfa,0x7e2b,0x7e59,0x7e86,0x7eaf,0x7ed7,0x7efc,0x7f1e,0x7f3e,0x7f5c,0x7f77,0x7f90,0x7fa6,0x7fba,0x7fcb,0x7fda,0x7fe6,0x7ff0,0x7ff8,0x7ffd,0x7fff,0x7fff,0x7ffd,0x7ff8,0x7ff0,0x7fe6,0x7fda,0x7fcb,0x7fba,0x7fa6,0x7f90,0x7f77,0x7f5c,0x7f3e,0x7f1e,0x7efc,0x7ed7,0x7eaf,0x7e86,0x7e59,0x7e2b,0x7dfa,0x7dc6,0x7d90,0x7d58,0x7d1e,0x7ce1,0x7ca1,0x7c60,0x7c1c,0x7bd5,0x7b8d,0x7b42,0x7af5,0x7aa5,0x7a53,0x79ff,0x79a9,0x7950,0x78f6,0x7899,0x783a,0x77d8,0x7775,0x770f,0x76a7,0x763e,0x75d2,0x7564,0x74f3,0x7481,0x740d,0x7397,0x731f,0x72a5,0x7228,0x71aa,0x712a,0x70a8,0x7025,0x6f9f,0x6f17,0x6e8e,0x6e03,0x6d76,0x6ce7,0x6c57,0x6bc5,0x6b31,0x6a9c,0x6a04,0x696c,0x68d1,0x6835,0x6798,0x66f9,0x6658,0x65b6,0x6513,0x646e,0x63c7,0x6320,0x6277,0x61cc,0x6121,0x6074,0x5fc5,0x5f16,0x5e65,0x5db3,0x5d00,0x5c4c,0x5b97,0x5ae1,0x5a29,0x5971,0x58b8,0x57fd,0x5742,0x5686,0x55c9,0x550b,0x544c,0x538d,0x52cd,0x520c,0x514a,0x5088,0x4fc5,0x4f01,0x4e3d,0x4d79,0x4cb3,0x4bee,0x4b28,0x4a61,0x499a,0x48d3,0x480b,0x4743,0x467b,0x45b2,0x44e9,0x4420,0x4357,0x428e,0x41c5,0x40fb,
0x4032,0x3f68,0x3e9f,0x3dd6,0x3d0c,0x3c43,0x3b7a,0x3ab1,0x39e9,0x3920,0x3858,0x3790,0x36c9,0x3601,0x353b,0x3474,0x33ae,0x32e9,0x3224,0x3160,0x309c,0x2fd9,0x2f16,0x2e54,0x2d93,0x2cd2,0x2c12,0x2b53,0x2a95,0x29d8,0x291b,0x285f,0x27a5,0x26eb,0x2632,0x257a,0x24c3,0x240d,0x2359,0x22a5,0x21f3,0x2141,0x2091,0x1fe2,0x1f35,0x1e88,0x1ddd,0x1d34,0x1c8b,0x1be4,0x1b3f,0x1a9a,0x19f8,0x1956,0x18b7,0x1818,0x177c,0x16e0,0x1647,0x15af,0x1519,0x1484,0x13f1,0x1360,0x12d0,0x1242,0x11b6,0x112c,0x10a4,0x101d,0x0f98,0x0f15,0x0e94,0x0e15,0x0d98,0x0d1d,0x0ca4,0x0c2d,0x0bb8,0x0b44,0x0ad3,0x0a64,0x09f7,0x098c,0x0923,0x08bd,0x0858,0x07f6,0x0796,0x0737,0x06dc,0x0682,0x062b,0x05d5,0x0583,0x0532,0x04e3,0x0497,0x044e,0x0406,0x03c1,0x037e,0x033e,0x0300,0x02c4,0x028a,0x0253,0x021f,0x01ec,0x01bd,0x018f,0x0164,0x013c,0x0115,0x00f2,0x00d0,0x00b2,0x0095,0x007b,0x0064,0x004f,0x003c,0x002c,0x001e,0x0013,0x000b,0x0004,0x0001,0x0000};
// windowing and prescaling
for (size_t i = 0; i < BUF_LEN; i++){
in[i] = S_MUL(frame[i],win[i]) << 4;
}

if ((cfg = kiss_fftr_alloc(BUF_LEN, 0/*is_inverse_fft*/, NULL, NULL)) == NULL){
printf("Not enough memory?\n");
return 1;
}else{
kiss_fftr(cfg, in, out);
free(cfg);
}

for (size_t k = 0; k < FRE_LEN; k++){
spec[k] = MAG(out[k].r,out[k].i);
}

return 0;
}

static int printMat(char *filename, kiss_fft_scalar *in, size_t N){
FILE *fp;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... There is no support currently for storing files. In principal, we could implement a filesystem that ships over to the SD card on the main board, but currently I believe it will always fail on fopen.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's what I suspected, too. I plan to replace these printMat with the appropriate Signpost's memory API to log results. We'll definitely need to ship events out of the Signpost for more processing.


if (! (fp = fopen(filename, "a"))){
printf("Unable to open %s\n",filename);
return 1;
}

for (size_t i = 0; i < N-1; i++){
//fprintf(fp, "%d,",in[i]);
fprintf(fp, "%f,",(double)in[i]/(double)SAMP_MAX);
}
//fprintf(fp, "%d\n",in[N-1]);
fprintf(fp, "%f\n",(double)in[N-1]/(double)SAMP_MAX);

fclose(fp);
return 0;
}

static int printMat2(char *filename, size_t *in, size_t N){
FILE *fp;

if (! (fp = fopen(filename, "a"))){
printf("Unable to open %s\n",filename);
return 1;
}

for (size_t i = 0; i < N-1; i++){
fprintf(fp, "%zu,",in[i]);
}
fprintf(fp, "%zu\n",in[N-1]);

fclose(fp);
return 0;
}