Skip to content

Commit f0f38ee

Browse files
phoddieMichael Kellner
authored andcommitted
add 32-bit output support for ESP32; document enqueue of raw samples
1 parent 4687858 commit f0f38ee

File tree

2 files changed

+47
-9
lines changed

2 files changed

+47
-9
lines changed

documentation/pins/audioout.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# AudioOut
22
Copyright 2018 Moddable Tech, Inc.
33

4-
Revised: February 23, 2018
4+
Revised: March 19, 2018
55

66
**Warning**: These notes are preliminary. Omissions and errors are likely. If you encounter problems, please ask for assistance.
77

@@ -124,9 +124,15 @@ The enqueue function has several different functions, all related to the audio q
124124
All invocations of the `enqueue` function include the first parameter, the target stream number, a value from 0 to one less than the number of streams configured using the constructor.
125125

126126
#### Enqueuing audio samples
127-
To `enqueue` audio samples call enqueue with a buffer of samples:
127+
Audio samples to play may be provided either as a MAUD audio resource or as raw audio samples. In both cases, the samples must be in the same format as the audio output (e.g. have the same sample rate, bits per sample, and number of channels).
128128

129-
audio.enqueue(0, buffer);
129+
To `enqueue` audio samples with a Moddable Audio header (MAUD), call enqueue with a buffer of samples:
130+
131+
audio.enqueue(0, AuioOut.Samples, buffer);
132+
133+
To `enqueue` a buffer of audio samples with no header call enqueue with a buffer of samples:
134+
135+
audio.enqueue(0, AudioOut.RawSamples, buffer);
130136

131137
To play the buffer several times, specify the optional `repeat` count. The repeat count is either a positive integer or `Infinity`.
132138

@@ -159,9 +165,9 @@ To change the volume, enqueue a `Volume` command on a stream. The volume command
159165
Values for the volume command range from 0 for silent, to 256 for full volume.
160166

161167
## MAUD format
162-
The `maud` format, "Moddable Audio", is a trivial uncompressed audio format intended to be compact and trivially parsed. The `enqueue` function of `AudioOut` class accepts only samples in the `maud` format.
168+
The `maud` format, "Moddable Audio", is a simple uncompressed audio format intended to be compact and trivially parsed. The `enqueue` function of `AudioOut` class accepts samples in the `maud` format. The `wav2maud` tool in the Moddable SDK converts WAV files to `maud` resources.
163169

164-
The format has a simple twelve byte header followed by samples.
170+
The format has a twelve byte header followed immediately by audio samples.
165171

166172
- offset 0 -- ASCII 'm'
167173
- offset 1 -- ASCII 'a'
@@ -187,6 +193,7 @@ The `audioOut` module is be configured at build time.
187193
- `MODDEF_AUDIOOUT_I2S_BCK_PIN` -- The I2S clock pin. The default is 26.
188194
- `MODDEF_AUDIOOUT_I2S_LR_PIN` -- The I2S LR pin. The default is 25.
189195
- `MODDEF_AUDIOOUT_I2S_DATAOUT_PIN` -- The I2S data pin. The default is 22.
196+
- `MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE` - Number of bits per sample to transmit over I2S, either 16 or 32. Default is 16.
190197

191198
### Defines for ESP8266
192199
- `MODDEF_AUDIOOUT_I2S_PDM` -- If zero, PCM samples are transmitted over I2S. If non-zero, samples are transmitted using PDM. Set to 32 for no oversampling, 64 for 2x oversampling, and 128 for 4x oversampling. Default is 0.

modules/pins/i2s/audioout.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
#ifndef MODDEF_AUDIOOUT_I2S_DATAOUT_PIN
4545
#define MODDEF_AUDIOOUT_I2S_DATAOUT_PIN (22)
4646
#endif
47+
#ifndef MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE
48+
#define MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE (16)
49+
#endif
4750
#endif
4851

4952
#ifndef MODDEF_AUDIOOUT_VOLUME_DIVIDER
@@ -132,7 +135,7 @@ typedef struct {
132135

133136
uint8_t state; // 0 idle, 1 playing, 2 terminated
134137

135-
uint32_t buffer[128];
138+
uint32_t buffer[512]; //@@ bigger when 32-bit samples
136139
#elif defined(__ets__)
137140
uint8_t i2sActive;
138141
OUTPUTSAMPLETYPE buffer[64]; // size assumes DMA Buffer size of I2S
@@ -306,7 +309,11 @@ void xs_audioout(xsMachine *the)
306309
out->state = kStateIdle;
307310
out->mutex = xSemaphoreCreateMutex();
308311

309-
xTaskCreate(audioOutLoop, "audioOut", 768, out, 7, &out->task);
312+
#if MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE == 16
313+
xTaskCreate(audioOutLoop, "audioOut", 1024, out, 7, &out->task);
314+
#else
315+
xTaskCreate(audioOutLoop, "audioOut", 2048, out, 7, &out->task);
316+
#endif
310317
#endif
311318
}
312319

@@ -627,9 +634,14 @@ void audioOutLoop(void *pvParameter)
627634
i2s_config_t i2s_config = {
628635
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
629636
.sample_rate = out->sampleRate,
630-
.bits_per_sample = 16,
637+
.bits_per_sample = MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE,
638+
#if MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE == 16
631639
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels
632640
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
641+
#else
642+
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT /* I2S_CHANNEL_FMT_RIGHT_LEFT */, // 2-channels
643+
.communication_format = I2S_COMM_FORMAT_I2S /* | I2S_COMM_FORMAT_I2S_MSB */,
644+
#endif
633645
.dma_buf_count = 2,
634646
.dma_buf_len = sizeof(out->buffer) / out->bytesPerFrame, // dma_buf_len is in frames, not bytes
635647
.use_apll = 0,
@@ -643,7 +655,6 @@ void audioOutLoop(void *pvParameter)
643655
};
644656
i2s_driver_install(MODDEF_AUDIOOUT_I2S_NUM, &i2s_config, 0, NULL);
645657
i2s_set_pin(MODDEF_AUDIOOUT_I2S_NUM, &pin_config);
646-
i2s_set_clk(MODDEF_AUDIOOUT_I2S_NUM, out->sampleRate, out->bitsPerSample, out->numChannels);
647658

648659
while (kStateTerminated != out->state) {
649660
if (kStateIdle == out->state) {
@@ -662,7 +673,27 @@ void audioOutLoop(void *pvParameter)
662673
audioMix(out, sizeof(out->buffer) / out->bytesPerFrame, (OUTPUTSAMPLETYPE *)out->buffer);
663674
xSemaphoreGive(out->mutex);
664675

676+
#if 16 == MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE
665677
i2s_write_bytes(MODDEF_AUDIOOUT_I2S_NUM, (const char *)out->buffer, sizeof(out->buffer), portMAX_DELAY);
678+
#elif 32 == MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE
679+
int samples = sizeof(out->buffer) / out->bytesPerFrame;
680+
int32_t samples32[64];
681+
int16_t *src = (int16_t *)out->buffer;
682+
while (samples) {
683+
int use = (samples >= 64) ? 64 : samples;
684+
int32_t *dst = samples32;
685+
int remain = use;
686+
687+
while (remain--)
688+
*dst++ = *src++ << 16;
689+
690+
samples -= use;
691+
692+
i2s_write_bytes(MODDEF_AUDIOOUT_I2S_NUM, (const char *)samples32, use << 2, portMAX_DELAY);
693+
}
694+
#else
695+
#error invalid MODDEF_AUDIOOUT_I2S_BITSPERSAMPLE
696+
#endif
666697
}
667698

668699
// from here, "out" is invalid

0 commit comments

Comments
 (0)