forked from W4D/soundcube-firmware
Compare commits
16 Commits
v.0.0.5
...
sampler-si
| Author | SHA1 | Date | |
|---|---|---|---|
| 962c299e80 | |||
| cef092f494 | |||
| d44210ce59 | |||
| 7418cdfd35 | |||
| ad035e69e2 | |||
| d5ea2377f9 | |||
| b9130dc0ae | |||
| b4f504b21d | |||
| 6bcc37d3ee | |||
| 8858d41806 | |||
| ffdbe9fa6d | |||
| 6ef94390a5 | |||
| 6dd3acac2f | |||
| 4a04e95757 | |||
| 06642bd570 | |||
| 07323ec068 |
70
README.md
70
README.md
@ -13,9 +13,8 @@
|
||||
## SD Card contents
|
||||
|
||||
|
||||
´´´
|
||||
```
|
||||
config.txt
|
||||
click.wav
|
||||
sound/
|
||||
├─ 1.wav
|
||||
├─ 2.wav
|
||||
@ -25,8 +24,11 @@ sound/
|
||||
├─ 6.wav
|
||||
├─ 7.wav
|
||||
├─ 8.wav
|
||||
ui/
|
||||
├─ click.wav
|
||||
├─ beep.wav
|
||||
|
||||
´´´
|
||||
```
|
||||
|
||||
### config.txt
|
||||
|
||||
@ -34,45 +36,89 @@ Json formatted config file. For now only edge led color working.
|
||||
|
||||
```
|
||||
{
|
||||
"boxid":"",
|
||||
"edge": {
|
||||
"color":{
|
||||
"r":50,
|
||||
"g":0,
|
||||
"b":0
|
||||
"idle":{
|
||||
"r":50,
|
||||
"g":0,
|
||||
"b":50
|
||||
},
|
||||
"active":{
|
||||
"r":0,
|
||||
"g":50,
|
||||
"b":50
|
||||
}
|
||||
}
|
||||
},
|
||||
"ring": {
|
||||
"color":{
|
||||
"idle":{
|
||||
"r":0,
|
||||
"g":50,
|
||||
"b":50
|
||||
},
|
||||
"active":{
|
||||
"r":0,
|
||||
"g":80,
|
||||
"b":50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### click.wav
|
||||
## Sound
|
||||
|
||||
Put a file called `click.wav` (not longer than a few seconds) in the root of the SD card and it will play when you press a button.
|
||||
### UI
|
||||
|
||||
There are two UI sounds - `click.wav` and `beep.wav`.
|
||||
|
||||
- `click.wav` is played when a button is pressed other than the select button
|
||||
- `beep.wav` is played when the rotary encoder select button is pressed
|
||||
|
||||
Put these files in the subfolder `ui` on the SD card.
|
||||
|
||||
#### Wave File Format for UI Sounds
|
||||
|
||||
`click.wav` needs to be **22050Hz (22kHz) 16bit Mono**
|
||||
|
||||
|
||||
### Sounds
|
||||
### Audio Out or Speakers
|
||||
|
||||
Put all sounds into the ´/sound´ subfolder. Name them ´1.wav, 2.wav, 3.wav...´
|
||||
Put all sounds into the `/sound` subfolder. Name them `1.wav, 2.wav, 3.wav...`
|
||||
|
||||
|
||||
## Wave File Format
|
||||
#### Wave File Format for Audio Out and Speakers
|
||||
|
||||
Export all sounds as **48000Hz (48kHz) 16bit Stereo**.
|
||||
|
||||
Export all sounds except ´click.wav´ as **48000Hz (48kHz) 16bit Stereo**.
|
||||
|
||||
### Audacity
|
||||
|
||||
You can use Audacity to export all soundfiles to WAV format.
|
||||
|
||||
Download it here [Github](https://github.com/audacity/audacity/releases)
|
||||
|
||||
1. Load file into Audacity
|
||||
2. Select Track
|
||||
3. File -> Export Audio
|
||||
|
||||
#### Format options in Audacity
|
||||
|
||||
**Audio Out**
|
||||
|
||||
- WAV(Microsoft)
|
||||
- Chanels: Stereo
|
||||
- Samplerate: 48000Hz
|
||||
- Encoding: Signed 16-bit PCM
|
||||
|
||||
**UI**
|
||||
|
||||
- WAV(Microsoft)
|
||||
- Channels: Mono
|
||||
- Samplerate: 22050Hz
|
||||
- Encoding: Signed 16-bit PCM
|
||||
|
||||
----
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
template<typename T>
|
||||
class RingBuffer{
|
||||
public:
|
||||
RingBuffer() {}
|
||||
@ -11,10 +10,10 @@ class RingBuffer{
|
||||
}
|
||||
|
||||
void begin(){
|
||||
buffer = new T[bufferSize];
|
||||
buffer = new int16_t[bufferSize];
|
||||
}
|
||||
|
||||
bool push(T data){
|
||||
bool push(int16_t data){
|
||||
if(counter < bufferSize){
|
||||
buffer[write] = data;
|
||||
write++; // % bufferSize;
|
||||
@ -25,8 +24,8 @@ class RingBuffer{
|
||||
return false;
|
||||
}
|
||||
|
||||
T pop(){
|
||||
T retval = 0;
|
||||
int16_t pop(){
|
||||
int16_t retval = 0;
|
||||
if(counter > 0) {
|
||||
counter--;
|
||||
retval = buffer[read];
|
||||
@ -36,22 +35,72 @@ class RingBuffer{
|
||||
return retval;
|
||||
}
|
||||
|
||||
int16_t* getReadPointer(){
|
||||
return &buffer[read];
|
||||
}
|
||||
|
||||
void pointerPop(int nbytes){
|
||||
for(int i=0; i<nbytes / 2; ++i){
|
||||
if(counter > 0) {
|
||||
counter--;
|
||||
read++;// % bufferSize;
|
||||
if(read == bufferSize) read = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pushDMA(int32_t *source){
|
||||
if(counter < bufferSize){
|
||||
rp2040.memcpyDMA(&buffer[write], source, 4);
|
||||
write += 2; // % bufferSize;
|
||||
if(write == bufferSize) write = 0;
|
||||
counter += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void* getWritePointer(){
|
||||
return &buffer[write];
|
||||
}
|
||||
|
||||
void advance(int nbytes){
|
||||
for(int i = 0; i < nbytes/2; ++i){
|
||||
if(counter < (bufferSize-1)){
|
||||
write++; // % bufferSize;
|
||||
if(write == bufferSize) write = 0;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void popDMA(int32_t *target){
|
||||
if(counter > 1) {
|
||||
counter -= 2;
|
||||
rp2040.memcpyDMA(target, &buffer[read], 4);
|
||||
read += 2;
|
||||
if(read >= bufferSize) read = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool isEmpty(){
|
||||
return counter == 0;
|
||||
}
|
||||
|
||||
bool isFull(){
|
||||
return counter == bufferSize;
|
||||
return counter == (bufferSize-2);
|
||||
}
|
||||
|
||||
int size(){
|
||||
return counter;
|
||||
}
|
||||
|
||||
int remains(){
|
||||
return (bufferSize-2) - counter;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t bufferSize = 0;
|
||||
int counter = 0;
|
||||
int write = 0;
|
||||
int read = 0;
|
||||
T *buffer;
|
||||
int16_t *buffer;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -74,16 +74,53 @@ struct WaveFile{
|
||||
return true;
|
||||
}
|
||||
|
||||
void readblock(){
|
||||
bool readblock(){
|
||||
uint8_t samplebyte[blockalign];
|
||||
|
||||
file.read(samplebyte, blockalign);
|
||||
|
||||
if(!file.available() && loop) file.seek(44, SeekSet);
|
||||
if(!file.available() && !loop) {
|
||||
file.seek(44, SeekSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < blockalign; i+=2){
|
||||
int16_t sample = (samplebyte[i+1] << 8) + samplebyte[i];
|
||||
buffer.push(sample);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readblockDMA(){
|
||||
void *bufferStart = buffer.getWritePointer();
|
||||
|
||||
file.read((uint8_t*)bufferStart, 4);
|
||||
buffer.advance(4);
|
||||
|
||||
if(!file.available() && loop) file.seek(44, SeekSet);
|
||||
if(!file.available() && !loop) {
|
||||
file.seek(44, SeekSet);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readblockSD(){
|
||||
void *bufferStart = buffer.getWritePointer();
|
||||
remains = buffer.remains();
|
||||
|
||||
if(remains > 512){
|
||||
adv = file.readBytes((char*)bufferStart, 512);
|
||||
buffer.advance(adv);
|
||||
|
||||
if(!file.available() && loop) file.seek(44, SeekSet);
|
||||
if(!file.available() && !loop) {
|
||||
file.seek(44, SeekSet);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t get(){
|
||||
@ -94,6 +131,10 @@ struct WaveFile{
|
||||
file.seek(44, SeekSet);
|
||||
}
|
||||
|
||||
void reset(){
|
||||
file.seek(44, SeekSet);
|
||||
}
|
||||
|
||||
bool loop = false;
|
||||
uint16_t format = 0;
|
||||
uint32_t length = 0;
|
||||
@ -102,7 +143,10 @@ struct WaveFile{
|
||||
uint16_t blockalign = 0;
|
||||
uint16_t bitspersample = 0;
|
||||
|
||||
RingBuffer<int16_t> buffer;
|
||||
int adv = 0;
|
||||
int remains = 0;
|
||||
|
||||
RingBuffer buffer;
|
||||
};
|
||||
|
||||
class WaveStream{
|
||||
@ -110,7 +154,7 @@ class WaveStream{
|
||||
WaveStream(){}
|
||||
|
||||
void begin(){
|
||||
wavefile.buffer.setSize(24000);
|
||||
wavefile.buffer.setSize(2048);
|
||||
wavefile.buffer.begin();
|
||||
}
|
||||
|
||||
@ -121,7 +165,10 @@ class WaveStream{
|
||||
|
||||
void toggle(){playing = !playing;}
|
||||
|
||||
void play(){playing = true;}
|
||||
void play(bool reset = false){
|
||||
if(reset) wavefile.reset();
|
||||
playing = true;
|
||||
}
|
||||
|
||||
void stop(){
|
||||
playing = false;
|
||||
@ -131,12 +178,18 @@ class WaveStream{
|
||||
void pause(){playing = false;}
|
||||
|
||||
void stream(){
|
||||
if(!wavefile.buffer.isFull() && playing){
|
||||
int cnt = 0;
|
||||
while (!wavefile.buffer.isFull() && cnt < 6000) {
|
||||
wavefile.readblock();
|
||||
cnt++;
|
||||
}
|
||||
int cnt = 0;
|
||||
while (!wavefile.buffer.isFull() && cnt < 1024) {
|
||||
bool ok = wavefile.readblockDMA();
|
||||
if(!ok) playing = false;
|
||||
cnt += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void streamChunk(){
|
||||
if(!wavefile.buffer.isFull()){
|
||||
bool ok = wavefile.readblockSD();
|
||||
if(!ok) playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,6 +197,16 @@ class WaveStream{
|
||||
return playing ? wavefile.get() : 0;
|
||||
}
|
||||
|
||||
int16_t* getPointer(){
|
||||
int16_t* p = wavefile.buffer.getReadPointer();
|
||||
wavefile.buffer.pointerPop(2);
|
||||
return p;
|
||||
}
|
||||
|
||||
void getDMA(int32_t *samples){
|
||||
if(playing) wavefile.buffer.popDMA(samples);
|
||||
}
|
||||
|
||||
bool isPlaying(){
|
||||
return playing;
|
||||
}
|
||||
@ -151,6 +214,7 @@ class WaveStream{
|
||||
//private:
|
||||
|
||||
WaveFile wavefile;
|
||||
uint32_t cuelist[16];
|
||||
|
||||
bool playing = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user