Skip to content

Commit 8985370

Browse files
committed
Initial implementation with ModPlayer submodule dependency and example project
1 parent cc6eef0 commit 8985370

13 files changed

+309
-0
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.idea/
2+
3+
build/
4+
cmake-build-*/
5+
6+
managed_components/
7+
dependencies.lock
8+
sdkconfig
9+
sdkconfig.old
10+
spiffs.bin

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "libs/ModPlayer"]
2+
path = libs/ModPlayer
3+
url = https://github.com/realsba/ModPlayer

CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
idf_component_register(
2+
SRCS
3+
"libs/ModPlayer/src/Arpeggio.cpp"
4+
"libs/ModPlayer/src/Module.cpp"
5+
"libs/ModPlayer/src/Channel.cpp"
6+
"libs/ModPlayer/src/CombinedEffect.cpp"
7+
"libs/ModPlayer/src/SlideDown.cpp"
8+
"libs/ModPlayer/src/SlideToNote.cpp"
9+
"libs/ModPlayer/src/SlideUp.cpp"
10+
"libs/ModPlayer/src/Vibrato.cpp"
11+
"libs/ModPlayer/src/VolumeSlide.cpp"
12+
INCLUDE_DIRS
13+
"libs/ModPlayer/src"
14+
)

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# ESP32 MOD Player Component
2+
3+
The `esp32-mod-player` is an ESP-IDF component designed to enable MOD file playback on ESP32 devices using the [ModPlayer](https://github.com/realsba/ModPlayer) library as a submodule.
4+
5+
## Features
6+
7+
- Provides seamless MOD file playback on ESP32 with minimal setup
8+
- Built as an esp-idf component, making it compatible with the ESP-IDF build system
9+
- Utilizes the `ModPlayer` library without additional code, ensuring efficient integration
10+
11+
## Requirements
12+
13+
- ESP-IDF v5.4 or later
14+
- ModPlayer library (included as a submodule)
15+
16+
## Installation
17+
18+
To include `esp32-mod-player` in your project:
19+
20+
1. Use the `idf.py add-dependency` command to add the component from the ESP Component Registry:
21+
22+
```bash
23+
idf.py add-dependency "realsba/esp32-mod-player"
24+
```
25+
26+
2. Alternatively, add the component manually as a Git submodule:
27+
28+
```bash
29+
git submodule add https://github.com/realsba/esp32-mod-player components/esp32-mod-player
30+
git submodule update --init --recursive
31+
```
32+
33+
3. Ensure that the `ModPlayer` library is correctly initialized as a submodule.
34+
35+
## Configuration
36+
37+
No specific configuration is required for this component. However, check the `ModPlayer` documentation for any settings or tuning parameters.
38+
39+
## Usage
40+
41+
A sample usage example is available in the [`examples/mod-player`](examples/mod-player) folder. This example demonstrates how to set up MOD playback within an esp-idf project.
42+
43+
## Contributing
44+
45+
Contributions are welcome! Feel free to open an issue or submit a pull request.
46+
47+
## License
48+
49+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/realsba/esp32-mod-player/blob/main/LICENSE) file for details.
50+
51+
## Author
52+
53+
- Bohdan Sadovyak
54+
55+
## Bugs/Issues
56+
57+
Please report any bugs or issues [here](https://github.com/realsba/esp32-mod-player/issues)

examples/mod-player/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
cmake_minimum_required(VERSION 3.25)
2+
3+
set(CMAKE_CXX_STANDARD 23)
4+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
5+
6+
set(EXTRA_COMPONENT_DIRS
7+
"../../"
8+
)
9+
10+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
11+
12+
project(mod-player)

examples/mod-player/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# MOD Player Example for ESP32
2+
3+
This example demonstrates how to use the `esp32-mod-player` component to enable MOD file playback on ESP32 devices.
4+
5+
## Overview
6+
7+
The example leverages the `esp32-mod-player` component, which integrates the [ModPlayer](https://github.com/realsba/ModPlayer) library as a submodule to provide efficient MOD file playback capabilities for ESP32.
8+
9+
## Requirements
10+
11+
- ESP32 device
12+
- ESP-IDF v5.4 or later
13+
- `esp32-mod-player` component (included in the main repository)
14+
15+
## Setup
16+
17+
1. **Build the example** in the main project directory:
18+
19+
```bash
20+
idf.py build
21+
```
22+
23+
2. **Flash the example** to your ESP32 device:
24+
25+
```bash
26+
idf.py -p /dev/ttyUSB0 flash monitor
27+
```
28+
29+
> Replace `/dev/ttyUSB0` with the port your ESP32 is connected to.
30+
31+
3. MOD Files:
32+
33+
The example includes a `mod` subdirectory containing MOD files sourced from [modarchive.org](https://modarchive.org/). This directory is used to demonstrate MOD playback functionality.
34+
35+
To load the MOD files onto your ESP32, create and flash a SPIFFS partition using the following commands:
36+
37+
```bash
38+
python $IDF_PATH/components/spiffs/spiffsgen.py 131072 mod spiffs.bin
39+
python $IDF_PATH/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 115200 write_flash -z 0x200000 spiffs.bin
40+
```
41+
42+
## Usage
43+
44+
After flashing, the example will automatically start MOD playback using the `esp32-mod-player` component. Monitor the serial output for playback status and debug information.
45+
46+
## Notes
47+
48+
- This example is part of the `esp32-mod-player` repository. For details on configuring `ModPlayer`, consult the library’s documentation.
49+
50+
## License
51+
52+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/realsba/esp32-mod-player/blob/main/LICENSE) file for details.
53+
54+
## Author
55+
56+
- Bohdan Sadovyak
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
idf_component_register(
2+
SRCS "main.cpp"
3+
REQUIRES nvs_flash spiffs esp_driver_i2s esp_driver_gpio esp32-mod-player
4+
INCLUDE_DIRS "."
5+
)
6+
7+
#spiffs_create_partition_image(storage ../storage FLASH_IN_PROJECT)

examples/mod-player/main/main.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include "Module.hpp"
2+
3+
#include <nvs_flash.h>
4+
#include <esp_log.h>
5+
#include <driver/i2s_std.h>
6+
7+
#include <freertos/FreeRTOS.h>
8+
#include <freertos/queue.h>
9+
#include <esp_spiffs.h>
10+
11+
constexpr auto TAG = "mod-player";
12+
constexpr int MIXER_FREQUENCY = 44100;
13+
constexpr int BUFFER_SIZE = 1024;
14+
char buff[8192];
15+
16+
Module module(MIXER_FREQUENCY);
17+
QueueHandle_t tx_queue;
18+
19+
bool IRAM_ATTR i2s_tx_buffer_callback(i2s_chan_handle_t handle, i2s_event_data_t* eventData, void* userData)
20+
{
21+
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
22+
xQueueSendFromISR(tx_queue, &eventData->size, &xHigherPriorityTaskWoken);
23+
return (xHigherPriorityTaskWoken == pdTRUE);
24+
}
25+
26+
void i2s_fill_buffer(void* audioBuffer, size_t len)
27+
{
28+
auto buffer = static_cast<int16_t*>(audioBuffer);
29+
size_t samples = len / sizeof(int16_t);
30+
auto volumeFactor = 0.06f;
31+
32+
for (size_t i = 0; i < samples; ++i) {
33+
buffer[i] = static_cast<int16_t>(module.getFrame() * volumeFactor);
34+
}
35+
}
36+
37+
extern "C" void app_main()
38+
{
39+
ESP_LOGI(TAG, "Start");
40+
41+
esp_vfs_spiffs_conf_t spiffsConfig = {
42+
.base_path = "/spiffs",
43+
.partition_label = nullptr,
44+
.max_files = 5,
45+
.format_if_mount_failed = true
46+
};
47+
48+
esp_err_t res = esp_vfs_spiffs_register(&spiffsConfig);
49+
if (res != ESP_OK) {
50+
if (res == ESP_FAIL) {
51+
ESP_LOGE(TAG, "Failed to mount or format filesystem");
52+
} else if (res == ESP_ERR_NOT_FOUND) {
53+
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
54+
} else {
55+
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(res));
56+
}
57+
return;
58+
}
59+
60+
module.load("/spiffs/popcor10.mod");
61+
62+
tx_queue = xQueueCreate(10, sizeof(size_t));
63+
if (tx_queue == nullptr) {
64+
ESP_LOGE(TAG, "Failed to create tx_queue");
65+
return;
66+
}
67+
68+
i2s_chan_handle_t channel;
69+
i2s_chan_config_t channelConfig = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
70+
channelConfig.dma_frame_num = 2048;
71+
ESP_ERROR_CHECK(i2s_new_channel(&channelConfig, &channel, nullptr));
72+
73+
i2s_std_config_t stdConfig = {
74+
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(MIXER_FREQUENCY),
75+
.slot_cfg = {
76+
.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
77+
.slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT,
78+
.slot_mode = I2S_SLOT_MODE_MONO,
79+
.slot_mask = I2S_STD_SLOT_BOTH,
80+
.ws_width = 16,
81+
.ws_pol = false,
82+
.bit_shift = false,
83+
#if !SOC_I2S_HW_VERSION_1
84+
.left_align = false,
85+
.big_endian = false,
86+
.bit_order_lsb = false,
87+
#endif
88+
.msb_right = false
89+
},
90+
.gpio_cfg = {
91+
.mclk = GPIO_NUM_0,
92+
.bclk = GPIO_NUM_26,
93+
.ws = GPIO_NUM_25,
94+
.dout = GPIO_NUM_22,
95+
.din = I2S_GPIO_UNUSED,
96+
.invert_flags = {
97+
.mclk_inv = 0,
98+
.bclk_inv = 0,
99+
.ws_inv = 0
100+
}
101+
},
102+
};
103+
104+
ESP_ERROR_CHECK(i2s_channel_init_std_mode(channel, &stdConfig));
105+
106+
i2s_event_callbacks_t callbacks = {
107+
.on_recv = nullptr,
108+
.on_recv_q_ovf = nullptr,
109+
.on_sent = i2s_tx_buffer_callback,
110+
.on_send_q_ovf = nullptr
111+
};
112+
ESP_ERROR_CHECK(i2s_channel_register_event_callback(channel, &callbacks, nullptr));
113+
114+
ESP_ERROR_CHECK(i2s_channel_enable(channel));
115+
116+
size_t dmaSize = 0;
117+
while (true) {
118+
if (xQueueReceive(tx_queue, &dmaSize, portMAX_DELAY)) {
119+
size_t bytesWritten = 0;
120+
size_t size = std::min(dmaSize, sizeof(buff));
121+
i2s_fill_buffer(buff, size);
122+
if (i2s_channel_write(channel, buff, size, &bytesWritten, 1000) == ESP_OK) {
123+
printf("Write Task: i2s write %d bytes\n", bytesWritten);
124+
} else {
125+
printf("Write Task: i2s write failed\n");
126+
}
127+
}
128+
}
129+
}

examples/mod-player/mod/popcor10.mod

61.6 KB
Binary file not shown.

examples/mod-player/partitions.csv

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
2+
3+
# Name, Type, SubType, Offset, Size, Flags
4+
nvs, data, nvs, 0x9000, 0x6000,
5+
phy_init, data, phy, 0xf000, 0x1000,
6+
factory, app, factory, 0x10000, 1M,
7+
storage, data, spiffs, 0x200000, 128K,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# This file was generated using idf.py save-defconfig. It can be edited manually.
2+
# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration
3+
#
4+
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
5+
CONFIG_PARTITION_TABLE_CUSTOM=y
6+
CONFIG_COMPILER_CXX_EXCEPTIONS=y

idf_component.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: "0.0.1"
2+
description: "ESP32 MOD Player"
3+
url: "https://github.com/realsba/esp32-mod-player"
4+
license: "MIT"
5+
dependencies:
6+
idf:
7+
version: "^5.4.0"

libs/ModPlayer

Submodule ModPlayer added at 02a9286

0 commit comments

Comments
 (0)