Skip to content

Commit

Permalink
Merge pull request #1 from trylaarsdam/recording
Browse files Browse the repository at this point in the history
I2S recording functionality
  • Loading branch information
trylaarsdam authored Jun 30, 2023
2 parents 7acba71 + 5ba72aa commit 599cd0c
Show file tree
Hide file tree
Showing 11 changed files with 33,206 additions and 65 deletions.
44 changes: 23 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,35 @@ i2s.begin();
```

3. Play your audio buffer

You can do this 2 ways - either by create a 16bit audio buffer and calling play:
```cpp
i2s.play(buffer, bufferLen);
```

Or you can also read a raw 8-bit PCM audio file, and provide it directly to i2s.play:
```cpp
#include "audio-file.h"
i2s.play(audio_file_raw, audio_file_raw_len);
```

You can see documentation on how to generate such a file [here](https://dev.toddr.org/i2s-audio-playback-on-the-portenta-h7/).

4. Record audio into a 32bit buffer
```cpp
#define BUFFER_LEN 8000 * 5 * 2 // 8000hz for 5 seconds for 2 channels (left, right)
uint32_t rxBuffer[BUFFER_LEN];
i2s.record(rxBuffer, BUFFER_LEN);
```
## Examples
A simple example is provided for both the M4 and M7 cores (usage is the same no matter the core), which plays a sine wave at 440.0Hz (A4) out of the I2S2 interface.
Two examples are provided - one which generates and plays back a simple sine wave, and one which plays back a prerecorded audio file, records audio from the microphone immediately after, and then sends the recorded audio to the serial port, where you can parse it with Audacity as 32-bit signed PCM stereo audio. You can find a full guide [here](https://dev.toddr.org/i2s-audio-playback-on-the-portenta-h7/).
## Playing audio files
To play audio files, you can follow these steps:
1. Open the audio file you want to play in Audacity (or equivalent software)
2. Save the file as a RAW file with no headers, and in 16-bit signed PCM format
2. Save the file as a RAW file with no headers, and in 8-bit signed PCM format
3. On Linux/Mac, use `xxd` to create a C header file
```bash
xxd -i {your-file.raw} > audio-file.h
Expand All @@ -51,23 +69,7 @@ xxd -i {your-file.raw} > audio-file.h
5. Configure the I2S library with a sample rate that matches your audio file (important, otherwise your file will play at the wrong speed)
6. Use this code to play the file
```cpp
// calculate number of samples in the file
unsigned int numSamples = audio_file_raw_len / 2;
// Allocate memory for stereo samples (2 times larger than mono samples)
uint16_t stereoSamples[8192];

for(int i = 0; i < numSamples; i++){
int16_t rawSample = audio_file_raw[i * 2] | (audio_file_raw[i * 2 + 1] << 8);
// convert to unsigned 16 bit sample
stereoSamples[(i * 2) % 8192] = rawSample;
stereoSamples[((i * 2) % 8192) + 1] = rawSample;

if((i * 2) % 8192 == 0 && i != 0)
{
HAL_I2S_Transmit(&hi2s2, stereoSamples, 8192, HAL_MAX_DELAY);
delay(8192 / 22050 * 1000); // replace 22050 with your sample rate
}
}
```
#include "audio-file.h"

This will be an example soon.
i2s.play(your_file_raw, your_file_raw_len);
```
2,004 changes: 2,004 additions & 0 deletions examples/playback-record/ARTiE_Fork.h

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions examples/playback-record/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "Arduino.h"
#include <portenta-i2s.h>
#include "ARTiE_Fork.h"

#define I2S_BUFFER_SIZE I2S_AUDIOFREQ_8K * 5 * 2 // 5 seconds of audio, 2 channels

PortentaI2S i2s = PortentaI2S(USE_I2S2, I2S_AUDIOFREQ_8K);

uint32_t myBuffer[I2S_BUFFER_SIZE]; // RX buffer

void setup()
{
Serial.begin(115200); // Begin serial communication to send file
i2s.begin(); // Initialize I2S

i2s.play(ARTiE_Fork_raw, ARTiE_Fork_raw_len); // Play the 8bit signed audio file
i2s.record(myBuffer, I2S_BUFFER_SIZE); // Record 32bit signed audio into myBuffer for 5 seconds

// Print the recorded audio to serial
for(int i = 0; i < I2S_BUFFER_SIZE; i++)
{
if(i % 1 == 0) // 2 words per sample, 2 channels of data
{
volatile uint32_t sample = myBuffer[i];
sample = sample >> 14;
int32_t signedSample = sample; // center at 0
signedSample = sample * 65534; // scale to 32bit from 16bit
Serial.write((uint8_t*)&signedSample, sizeof(int32_t));
}
}
}

void loop()
{
// do nothing
}
24 changes: 24 additions & 0 deletions examples/playback-record/platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:portenta_h7_m7]
build_type = debug
platform = ststm32
board = portenta_h7_m7
framework = arduino
upload_protocol = stlink
debug_tool = stlink

[env:portenta_h7_m4]
platform = ststm32
board = portenta_h7_m4
framework = arduino
upload_protocol = stlink
debug_tool = stlink
43 changes: 3 additions & 40 deletions examples/sine-wave/main.cpp
Original file line number Diff line number Diff line change
@@ -1,39 +1,3 @@
#ifdef CORE_CM4

#include <Arduino.h>
#include <portenta-i2s.h>

PortentaI2S i2s = PortentaI2S(USE_I2S2, I2S_AUDIOFREQ_44K);

void setup()
{
i2s.begin();

// Generate sine wave
uint16_t samplebuf[8192]; // for both left + right
const int nsamples = sizeof(samplebuf) / sizeof(samplebuf[0]);
int i = 0;
while(i < nsamples) {
double t = ((double)i / 2.0) / 44100.0;
samplebuf[i] = 32767*((sin((440.0 - FREQ_OFFSET) * TAU * t) / 2)); // left channel
samplebuf[i+1] = samplebuf[i]; // right channel (same)
i += 2;
}

// Play continuously
while(1){
i2s.play(samplebuf, nsamples);
}
}

void loop()
{

}

#endif

#ifdef CORE_CM7
#include "Arduino.h"
#include <portenta-i2s.h>

Expand All @@ -45,12 +9,12 @@ void setup()
i2s.begin();

// Generate sine wave
uint16_t samplebuf[8192]; // for both left + right
uint32_t samplebuf[8192]; // for both left + right
const int nsamples = sizeof(samplebuf) / sizeof(samplebuf[0]);
int i = 0;
while(i < nsamples) {
double t = ((double)i / 2.0) / 44100.0;
samplebuf[i] = 32767*((sin((440.0 - FREQ_OFFSET) * TAU * t) / 2)); // left channel
samplebuf[i] = 65536*((sin((440.0 - FREQ_OFFSET) * TAU * t) / 2)); // left channel
samplebuf[i+1] = samplebuf[i]; // right channel (same)
i += 2;
}
Expand All @@ -64,5 +28,4 @@ void setup()
void loop()
{

}
#endif
}
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Portenta-I2S",
"version": "1.0.0",
"version": "1.1.0",
"description": "A simple I2S implementation using HAL for the Portenta H7 series.",
"repository":
{
Expand Down
Loading

0 comments on commit 599cd0c

Please sign in to comment.