audio/filters/source/main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <3ds.h>
#define SAMPLERATE 22050
#define SAMPLESPERBUF (SAMPLERATE / 30)
#define BYTESPERSAMPLE 4
#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
// audioBuffer is stereo PCM16
void fill_buffer(void* audioBuffer, size_t offset, size_t size, int frequency) {
u32* dest = (u32*) audioBuffer;
for (int i = 0; i < size; i++) {
// This is a simple sine wave, with a frequency of `frequency` Hz, and an amplitude 30% of maximum.
s16 sample = 0.3 * 0x7FFF * sin(frequency * (2 * M_PI) * (offset + i) / SAMPLERATE);
// Stereo samples are interleaved: left and right channels.
dest[i] = (sample << 16) | (sample & 0xffff);
}
DSP_FlushDataCache(audioBuffer, size);
}
int main(int argc, char **argv) {
gfxInitDefault();
PrintConsole topScreen;
consoleInit(GFX_TOP, &topScreen);
consoleSelect(&topScreen);
printf("libctru filtered streamed audio\n");
u32* audioBuffer = (u32*) linearAlloc(SAMPLESPERBUF * BYTESPERSAMPLE * 2);
bool fillBlock = false;
ndspInit();
ndspSetOutputMode(NDSP_OUTPUT_STEREO);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetRate(0, SAMPLERATE);
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16);
// Output at 100% on the first pair of left and right channels.
float mix[12];
memset(mix, 0, sizeof(mix));
mix[0] = 1.0;
mix[1] = 1.0;
ndspChnSetMix(0, mix);
// Note Frequencies
int notefreq[] = {
220,
440, 880, 1760, 3520, 7040,
14080,
7040, 3520, 1760, 880, 440
};
int note = 4;
// Filters
const char* filter_names[] = {
"None",
"Low-Pass",
"High-Pass",
"Band-Pass",
"Notch",
"Peaking"
};
int filter = 0;
// We set up two wave buffers and alternate between the two,
// effectively streaming an infinitely long sine wave.
ndspWaveBuf waveBuf[2];
memset(waveBuf,0,sizeof(waveBuf));
waveBuf[0].data_vaddr = &audioBuffer[0];
waveBuf[0].nsamples = SAMPLESPERBUF;
waveBuf[1].data_vaddr = &audioBuffer[SAMPLESPERBUF];
waveBuf[1].nsamples = SAMPLESPERBUF;
size_t stream_offset = 0;
fill_buffer(audioBuffer,stream_offset, SAMPLESPERBUF * 2, notefreq[note]);
stream_offset += SAMPLESPERBUF;
ndspChnWaveBufAdd(0, &waveBuf[0]);
ndspChnWaveBufAdd(0, &waveBuf[1]);
printf("Press up/down to change tone frequency\n");
printf("Press left/right to change filter\n");
printf("\x1b[6;1Hnote = %i Hz ", notefreq[note]);
printf("\x1b[7;1Hfilter = %s ", filter_names[filter]);
while(aptMainLoop()) {
gfxSwapBuffers();
gfxFlushBuffers();
gspWaitForVBlank();
hidScanInput();
u32 kDown = hidKeysDown();
if (kDown & KEY_START)
break; // break in order to return to hbmenu
if (kDown & KEY_DOWN) {
note--;
if (note < 0) {
note = ARRAY_SIZE(notefreq) - 1;
}
printf("\x1b[6;1Hnote = %i Hz ", notefreq[note]);
} else if (kDown & KEY_UP) {
note++;
if (note >= ARRAY_SIZE(notefreq)) {
note = 0;
}
printf("\x1b[6;1Hnote = %i Hz ", notefreq[note]);
}
bool update_params = false;
if (kDown & KEY_LEFT) {
filter--;
if (filter < 0) {
filter = ARRAY_SIZE(filter_names) - 1;
}
update_params = true;
} else if (kDown & KEY_RIGHT) {
filter++;
if (filter >= ARRAY_SIZE(filter_names)) {
filter = 0;
}
update_params = true;
}
if (update_params) {
printf("\x1b[7;1Hfilter = %s ", filter_names[filter]);
switch (filter) {
default:
ndspChnIirBiquadSetEnable(0, false);
break;
case 1:
ndspChnIirBiquadSetParamsLowPassFilter(0, 1760.f, 0.707f);
break;
case 2:
ndspChnIirBiquadSetParamsHighPassFilter(0, 1760.f, 0.707f);
break;
case 3:
ndspChnIirBiquadSetParamsBandPassFilter(0, 1760.f, 0.707f);
break;
case 4:
ndspChnIirBiquadSetParamsNotchFilter(0, 1760.f, 0.707f);
break;
case 5:
ndspChnIirBiquadSetParamsPeakingEqualizer(0, 1760.f, 0.707f, 3.0f);
break;
}
}
if (waveBuf[fillBlock].status == NDSP_WBUF_DONE) {
fill_buffer(waveBuf[fillBlock].data_pcm16, stream_offset, waveBuf[fillBlock].nsamples, notefreq[note]);
ndspChnWaveBufAdd(0, &waveBuf[fillBlock]);
stream_offset += waveBuf[fillBlock].nsamples;
fillBlock = !fillBlock;
}
}
ndspExit();
linearFree(audioBuffer);
gfxExit();
return 0;
}
3ds.h
Central 3DS header.
aptMainLoop
bool aptMainLoop(void)
Main function which handles sleep mode and HOME/power buttons - call this at the beginning of every f...
NDSP_FORMAT_STEREO_PCM16
@ NDSP_FORMAT_STEREO_PCM16
Buffer contains Stereo PCM16.
Definition: channel.h:29
ndspChnSetMix
void ndspChnSetMix(int id, float mix[12])
Sets the mix parameters (volumes) of a channel.
ndspChnWaveBufAdd
void ndspChnWaveBufAdd(int id, ndspWaveBuf *buf)
Adds a wave buffer to the wave buffer queue of a channel.
NDSP_INTERP_LINEAR
@ NDSP_INTERP_LINEAR
Linear interpolation.
Definition: channel.h:44
ndspChnIirBiquadSetParamsNotchFilter
bool ndspChnIirBiquadSetParamsNotchFilter(int id, float f0, float Q)
Sets the biquad to be a notch filter.
ndspChnSetRate
void ndspChnSetRate(int id, float rate)
Sets the sample rate of a channel.
ndspChnIirBiquadSetParamsLowPassFilter
bool ndspChnIirBiquadSetParamsLowPassFilter(int id, float f0, float Q)
Sets the biquad to be a low pass filter.
ndspChnSetFormat
void ndspChnSetFormat(int id, u16 format)
Sets the format of a channel.
ndspChnIirBiquadSetParamsHighPassFilter
bool ndspChnIirBiquadSetParamsHighPassFilter(int id, float f0, float Q)
Sets the biquad to be a high pass filter.
ndspChnIirBiquadSetParamsPeakingEqualizer
bool ndspChnIirBiquadSetParamsPeakingEqualizer(int id, float f0, float Q, float gain)
Sets the biquad to be a peaking equalizer.
ndspChnIirBiquadSetEnable
void ndspChnIirBiquadSetEnable(int id, bool enable)
Configures whether the IIR biquad filter of a channel is enabled.
ndspChnSetInterp
void ndspChnSetInterp(int id, ndspInterpType type)
Sets the interpolation type of a channel.
ndspChnIirBiquadSetParamsBandPassFilter
bool ndspChnIirBiquadSetParamsBandPassFilter(int id, float f0, float Q)
Sets the biquad to be a band pass filter.
consoleInit
PrintConsole * consoleInit(gfxScreen_t screen, PrintConsole *console)
Initialise the console.
consoleSelect
PrintConsole * consoleSelect(PrintConsole *console)
Make the specified console the render target.
DSP_FlushDataCache
Result DSP_FlushDataCache(const void *address, u32 size)
Flushes the cache.
gfxSwapBuffers
void gfxSwapBuffers(void)
Updates the configuration of both screens.
gfxInitDefault
void gfxInitDefault(void)
Initializes the LCD framebuffers with default parameters This is equivalent to calling:
GFX_TOP
@ GFX_TOP
Top screen.
Definition: gfx.h:26
gfxExit
void gfxExit(void)
Deinitializes and frees the LCD framebuffers.
gfxFlushBuffers
void gfxFlushBuffers(void)
Flushes the data cache for the current framebuffers.
gspWaitForVBlank
#define gspWaitForVBlank()
Waits for VBlank.
Definition: gspgpu.h:151
KEY_UP
@ KEY_UP
D-Pad Up or Circle Pad Up.
Definition: hid.h:37
KEY_LEFT
@ KEY_LEFT
D-Pad Left or Circle Pad Left.
Definition: hid.h:39
KEY_START
@ KEY_START
Start.
Definition: hid.h:15
KEY_RIGHT
@ KEY_RIGHT
D-Pad Right or Circle Pad Right.
Definition: hid.h:40
KEY_DOWN
@ KEY_DOWN
D-Pad Down or Circle Pad Down.
Definition: hid.h:38
hidKeysDown
u32 hidKeysDown(void)
Returns a bitmask of newly pressed buttons, this frame.
hidScanInput
void hidScanInput(void)
Scans HID for input data.
linearAlloc
void * linearAlloc(size_t size)
Allocates a 0x80-byte aligned buffer.
linearFree
void linearFree(void *mem)
Frees a buffer.
ndspSetOutputMode
void ndspSetOutputMode(ndspOutputMode mode)
Sets the output mode.
NDSP_WBUF_DONE
@ NDSP_WBUF_DONE
The wave buffer has finished being played.
Definition: ndsp.h:53
ndspExit
void ndspExit(void)
Exits NDSP.
NDSP_OUTPUT_STEREO
@ NDSP_OUTPUT_STEREO
Stereo sound.
Definition: ndsp.h:17
ndspInit
Result ndspInit(void)
Initializes NDSP.
PrintConsole
Console structure used to store the state of a console render context.
Definition: console.h:77
s16
int16_t s16
16-bit signed integer
Definition: types.h:27
u32
uint32_t u32
32-bit unsigned integer
Definition: types.h:23