Skip to content

Commit 9cd9df0

Browse files
phoddiemkellner
authored andcommitted
queued volume in audioout
1 parent d7e405a commit 9cd9df0

File tree

2 files changed

+185
-85
lines changed

2 files changed

+185
-85
lines changed

modules/pins/i2s/audioout.c

Lines changed: 180 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,14 @@
7878

7979
typedef struct {
8080
void *samples;
81-
int sampleCount; // 0 means this is a callback with value of (uintptr_t)samples
82-
int position;
83-
int repeat; // alwauys 1 for callback, negative for infinite
81+
int sampleCount; // 0 means this is a callback or volume command with value of (uintptr_t)samples
82+
int position; // less than zero if callback, else
83+
int repeat; // always 1 for callback, negative for infinite
8484
} modAudioQueueElementRecord, *modAudioQueueElement;
8585

8686
typedef struct {
87-
int elementCount;
87+
uint16_t volume; // 8.8 fixed
88+
int elementCount;
8889
modAudioQueueElementRecord element[MODDEF_AUDIOOUT_QUEUELENGTH];
8990
} modAudioOutStreamRecord, *modAudioOutStream;
9091

@@ -96,6 +97,7 @@ typedef struct {
9697
uint8_t numChannels;
9798
uint8_t bitsPerSample;
9899
uint8_t bytesPerFrame;
100+
uint8_t applyVolume; // one or more active streams is not at 1.0 volume
99101

100102
int activeStreamCount;
101103
modAudioOutStream activeStream[MODDEF_AUDIOOUT_STREAMS];
@@ -131,6 +133,7 @@ static void audioOutLoop(void *pvParameter);
131133
#endif
132134
static void audioMix(modAudioOut out, int samplesToGenerate, OUTPUTSAMPLETYPE *output);
133135
static void endOfElement(modAudioOut out, modAudioOutStream stream);
136+
static void setStreamVolume(modAudioOut out, modAudioOutStream stream, int volume);
134137

135138
void xs_audioout_destructor(void *data)
136139
{
@@ -216,6 +219,9 @@ void xs_audioout(xsMachine *the)
216219
out->bytesPerFrame = (bitsPerSample * numChannels) >> 3;
217220
out->streamCount = streamCount;
218221

222+
for (i = 0; i < streamCount; i++)
223+
out->stream[i].volume = 256;
224+
219225
#if defined(__APPLE__)
220226
OSStatus err;
221227
AudioStreamBasicDescription desc = {0};
@@ -286,11 +292,19 @@ void xs_audioout_stop(xsMachine *the)
286292
#endif
287293
}
288294

295+
enum {
296+
kKindSamples = 1,
297+
kKindFlush = 2,
298+
kKindCallback = 3,
299+
kKindVolume = 4
300+
};
301+
289302
void xs_audioout_enqueue(xsMachine *the)
290303
{
291304
modAudioOut out = xsmcGetHostData(xsThis);
292305
int stream, argc = xsmcArgc;
293-
int repeat = 1, sampleOffset = 0, samplesToUse = -1, bufferSamples;
306+
int repeat = 1, sampleOffset = 0, samplesToUse = -1, bufferSamples, volume;
307+
uint8_t kind;
294308
uint8_t *buffer;
295309
uint16_t sampleRate;
296310
uint8_t numChannels;
@@ -303,93 +317,119 @@ void xs_audioout_enqueue(xsMachine *the)
303317
if ((stream < 0) || (stream >= out->streamCount))
304318
xsRangeError("invalid stream");
305319

306-
if (MODDEF_AUDIOOUT_QUEUELENGTH == out->stream[stream].elementCount)
307-
xsUnknownError("queue full");
320+
kind = xsmcToInteger(xsArg(1));
321+
if (kKindFlush != kind) {
322+
if (MODDEF_AUDIOOUT_QUEUELENGTH == out->stream[stream].elementCount)
323+
xsUnknownError("queue full");
324+
}
325+
326+
switch (kind) {
327+
case kKindSamples:
328+
if (argc > 3) {
329+
if ((xsNumberType == xsmcTypeOf(xsArg(3))) && (C_INFINITY == xsmcToNumber(xsArg(3))))
330+
repeat = -1;
331+
else
332+
repeat = xsmcToInteger(xsArg(3));
333+
if (argc > 4) {
334+
sampleOffset = xsmcToInteger(xsArg(4));
335+
if (argc > 5)
336+
samplesToUse = xsmcToInteger(xsArg(5));
337+
}
338+
}
308339

309-
if (1 == argc) {
340+
buffer = xsmcGetHostData(xsArg(2));
341+
if (('m' != buffer[0]) || ('a' != buffer[1]) || (1 != buffer[2]))
342+
xsUnknownError("bad header");
343+
344+
bitsPerSample = c_read8(buffer + 3);
345+
sampleRate = c_read16(buffer + 4);
346+
numChannels = c_read8(buffer + 6);
347+
bufferSamples = c_read32(buffer + 8);
348+
if ((bitsPerSample != out->bitsPerSample) || (sampleRate != out->sampleRate) || (numChannels != out->numChannels))
349+
xsUnknownError("format doesn't match output");
350+
351+
buffer += 12;
352+
353+
if (sampleOffset >= bufferSamples)
354+
xsUnknownError("invalid offset");
355+
356+
if ((samplesToUse < 0) || ((sampleOffset + samplesToUse) > bufferSamples))
357+
samplesToUse = bufferSamples - sampleOffset;
358+
310359
#if defined(__APPLE__)
311-
pthread_mutex_lock(&out->mutex);
360+
pthread_mutex_lock(&out->mutex);
312361
#elif ESP32
313-
xSemaphoreTake(out->mutex, portMAX_DELAY);
362+
xSemaphoreTake(out->mutex, portMAX_DELAY);
314363
#endif
315-
out->stream[stream].elementCount = 0; // flush queue
364+
365+
element = &out->stream[stream].element[out->stream[stream].elementCount];
366+
element->samples = buffer + (sampleOffset * out->bytesPerFrame);
367+
element->sampleCount = samplesToUse;
368+
element->position = 0;
369+
element->repeat = repeat;
370+
371+
enqueue:
372+
out->stream[stream].elementCount += 1;
373+
374+
if (1 == out->stream[stream].elementCount)
375+
updateActiveStreams(out);
376+
316377
#if defined(__APPLE__)
317-
pthread_mutex_unlock(&out->mutex);
378+
pthread_mutex_unlock(&out->mutex);
318379
#elif ESP32
319-
xSemaphoreGive(out->mutex);
380+
xSemaphoreGive(out->mutex);
320381
#endif
321-
return;
322-
}
382+
break;
323383

324-
if ((2 == argc) && ((xsNumberType == xsmcTypeOf(xsArg(1))) || (xsIntegerType == xsmcTypeOf(xsArg(1))))) {
325-
// callback id
384+
case kKindFlush:
326385
#if defined(__APPLE__)
327-
pthread_mutex_lock(&out->mutex);
386+
pthread_mutex_lock(&out->mutex);
328387
#elif ESP32
329-
xSemaphoreTake(out->mutex, portMAX_DELAY);
388+
xSemaphoreTake(out->mutex, portMAX_DELAY);
330389
#endif
331-
element = &out->stream[stream].element[out->stream[stream].elementCount];
332-
element->samples = (void *)xsmcToInteger(xsArg(1));
333-
element->sampleCount = 0;
334-
element->position = 0;
335-
element->repeat = 1;
336-
goto done;
337-
}
338-
339-
if (argc > 2) {
340-
if ((xsNumberType == xsmcTypeOf(xsArg(2))) && (C_INFINITY == xsmcToNumber(xsArg(2))))
341-
repeat = -1;
342-
else
343-
repeat = xsmcToInteger(xsArg(2));
344-
if (argc > 3) {
345-
sampleOffset = xsmcToInteger(xsArg(3));
346-
if (argc > 4)
347-
samplesToUse = xsmcToInteger(xsArg(4));
348-
}
349-
}
350-
351-
buffer = xsmcGetHostData(xsArg(1));
352-
if (('m' != buffer[0]) || ('a' != buffer[1]) || (1 != buffer[2]))
353-
xsUnknownError("bad header");
354-
355-
bitsPerSample = c_read8(buffer + 3);
356-
sampleRate = c_read16(buffer + 4);
357-
numChannels = c_read8(buffer + 6);
358-
bufferSamples = c_read32(buffer + 8);
359-
if ((bitsPerSample != out->bitsPerSample) || (sampleRate != out->sampleRate) || (numChannels != out->numChannels))
360-
xsUnknownError("format doesn't match output");
361-
362-
buffer += 12;
363-
364-
if (sampleOffset >= bufferSamples)
365-
xsUnknownError("invalid offset");
366-
367-
if ((samplesToUse < 0) || ((sampleOffset + samplesToUse) > bufferSamples))
368-
samplesToUse = bufferSamples - sampleOffset;
369-
390+
out->stream[stream].elementCount = 0; // flush queue
370391
#if defined(__APPLE__)
371-
pthread_mutex_lock(&out->mutex);
392+
pthread_mutex_unlock(&out->mutex);
372393
#elif ESP32
373-
xSemaphoreTake(out->mutex, portMAX_DELAY);
394+
xSemaphoreGive(out->mutex);
374395
#endif
396+
break;
375397

376-
element = &out->stream[stream].element[out->stream[stream].elementCount];
377-
element->samples = buffer + (sampleOffset * out->bytesPerFrame);
378-
element->sampleCount = samplesToUse;
379-
element->position = 0;
380-
element->repeat = repeat;
381-
382-
done:
383-
out->stream[stream].elementCount += 1;
384-
385-
if (1 == out->stream[stream].elementCount)
386-
updateActiveStreams(out);
398+
case kKindCallback:
399+
#if defined(__APPLE__)
400+
pthread_mutex_lock(&out->mutex);
401+
#elif ESP32
402+
xSemaphoreTake(out->mutex, portMAX_DELAY);
403+
#endif
404+
element = &out->stream[stream].element[out->stream[stream].elementCount];
405+
element->samples = (void *)xsmcToInteger(xsArg(2));
406+
element->sampleCount = 0;
407+
element->position = -1;
408+
element->repeat = 0; //@@
409+
goto enqueue;
410+
411+
case kKindVolume:
412+
volume = xsmcToInteger(xsArg(2));
413+
if (0 == out->stream[stream].elementCount) {
414+
setStreamVolume(out, &out->stream[stream], volume);
415+
break;
416+
}
387417

388418
#if defined(__APPLE__)
389-
pthread_mutex_unlock(&out->mutex);
419+
pthread_mutex_lock(&out->mutex);
390420
#elif ESP32
391-
xSemaphoreGive(out->mutex);
421+
xSemaphoreTake(out->mutex, portMAX_DELAY);
392422
#endif
423+
element = &out->stream[stream].element[out->stream[stream].elementCount];
424+
element->samples = NULL;
425+
element->sampleCount = 0;
426+
element->position = volume;
427+
element->repeat = 0; //@@
428+
goto enqueue;
429+
430+
default:
431+
xsUnknownError("bad kind");
432+
}
393433

394434
xsResult = xsThis;
395435
}
@@ -400,10 +440,15 @@ void updateActiveStreams(modAudioOut out)
400440
int i;
401441

402442
out->activeStreamCount = 0;
443+
out->applyVolume = false;
403444
for (i = 0; i < out->streamCount; i++) {
404-
if (0 == out->stream[i].elementCount)
445+
modAudioOutStream stream = &out->stream[i];
446+
447+
if ((0 == stream->elementCount) || (0 == stream->volume))
405448
continue;
406-
out->activeStream[out->activeStreamCount++] = &out->stream[i];
449+
out->activeStream[out->activeStreamCount++] = stream;
450+
if (stream->volume != 256)
451+
out->applyVolume = true;
407452
}
408453
}
409454

@@ -585,7 +630,16 @@ void audioMix(modAudioOut out, int samplesToGenerate, OUTPUTSAMPLETYPE *output)
585630
int use = element->sampleCount - element->position;
586631
if (use > samplesToGenerate)
587632
use = samplesToGenerate;
588-
c_memcpy(output, (element->position * bytesPerFrame) + (uint8_t *)element->samples, use * bytesPerFrame);
633+
634+
OUTPUTSAMPLETYPE *s0 = (OUTPUTSAMPLETYPE *)((element->position * bytesPerFrame) + (uint8_t *)element->samples);
635+
if (!out->applyVolume)
636+
c_memcpy(output, s0, use * bytesPerFrame);
637+
else {
638+
uint16_t v0 = stream->volume;
639+
int count = use * out->numChannels;
640+
while (count--)
641+
*output++ = (*s0++ * v0) >> 8;
642+
}
589643

590644
output = (OUTPUTSAMPLETYPE *)((use * bytesPerFrame) + (uint8_t *)output);
591645
samplesToGenerate -= use;
@@ -611,8 +665,16 @@ void audioMix(modAudioOut out, int samplesToGenerate, OUTPUTSAMPLETYPE *output)
611665
OUTPUTSAMPLETYPE *s0 = (OUTPUTSAMPLETYPE *)((element0->position * bytesPerFrame) + (uint8_t *)element0->samples);
612666
OUTPUTSAMPLETYPE *s1 = (OUTPUTSAMPLETYPE *)((element1->position * bytesPerFrame) + (uint8_t *)element1->samples);
613667
int count = use * out->numChannels;
614-
while (count--)
615-
*output++ = *s0++ + *s1++;
668+
if (!out->applyVolume) {
669+
while (count--)
670+
*output++ = *s0++ + *s1++;
671+
}
672+
else {
673+
uint16_t v0 = stream0->volume;
674+
uint16_t v1 = stream1->volume;
675+
while (count--)
676+
*output++ = ((*s0++ * v0) + (*s1++ * v1)) >> 8;
677+
}
616678

617679
samplesToGenerate -= use;
618680
element0->position += use;
@@ -646,8 +708,17 @@ void audioMix(modAudioOut out, int samplesToGenerate, OUTPUTSAMPLETYPE *output)
646708
OUTPUTSAMPLETYPE *s1 = (OUTPUTSAMPLETYPE *)((element1->position * bytesPerFrame) + (uint8_t *)element1->samples);
647709
OUTPUTSAMPLETYPE *s2 = (OUTPUTSAMPLETYPE *)((element2->position * bytesPerFrame) + (uint8_t *)element2->samples);
648710
int count = use * out->numChannels;
649-
while (count--)
650-
*output++ = *s0++ + *s1++ + *s2++;
711+
if (!out->applyVolume) {
712+
while (count--)
713+
*output++ = *s0++ + *s1++ + *s2++;
714+
}
715+
else {
716+
uint16_t v0 = stream0->volume;
717+
uint16_t v1 = stream1->volume;
718+
uint16_t v2 = stream2->volume;
719+
while (count--)
720+
*output++ = ((*s0++ * v0) + (*s1++ * v1) + (*s2++ * v2)) >> 8;
721+
}
651722

652723
samplesToGenerate -= use;
653724
element0->position += use;
@@ -689,8 +760,18 @@ void audioMix(modAudioOut out, int samplesToGenerate, OUTPUTSAMPLETYPE *output)
689760
OUTPUTSAMPLETYPE *s2 = (OUTPUTSAMPLETYPE *)((element2->position * bytesPerFrame) + (uint8_t *)element2->samples);
690761
OUTPUTSAMPLETYPE *s3 = (OUTPUTSAMPLETYPE *)((element3->position * bytesPerFrame) + (uint8_t *)element3->samples);
691762
int count = use * out->numChannels;
692-
while (count--)
693-
*output++ = *s0++ + *s1++ + *s2++ + *s3++;
763+
if (!out->applyVolume) {
764+
while (count--)
765+
*output++ = *s0++ + *s1++ + *s2++ + *s3++;
766+
}
767+
else {
768+
uint16_t v0 = stream0->volume;
769+
uint16_t v1 = stream1->volume;
770+
uint16_t v2 = stream2->volume;
771+
uint16_t v3 = stream3->volume;
772+
while (count--)
773+
*output++ = ((*s0++ * v0) + (*s1++ * v1) + (*s2++ * v2) + (*s3++ * v3)) >> 8;
774+
}
694775

695776
samplesToGenerate -= use;
696777
element0->position += use;
@@ -732,8 +813,12 @@ void endOfElement(modAudioOut out, modAudioOutStream stream)
732813
element->repeat -= 1;
733814

734815
while (0 == element->repeat) {
735-
if (0 == element->sampleCount)
736-
queueCallback(out, (xsIntegerValue)element->samples);
816+
if (0 == element->sampleCount) {
817+
if (element->position < 0)
818+
queueCallback(out, (xsIntegerValue)element->samples);
819+
else
820+
setStreamVolume(out, stream, element->position);
821+
}
737822

738823
stream->elementCount -= 1;
739824
if (stream->elementCount)
@@ -744,3 +829,14 @@ void endOfElement(modAudioOut out, modAudioOutStream stream)
744829
}
745830
}
746831
}
832+
833+
void setStreamVolume(modAudioOut out, modAudioOutStream stream, int volume)
834+
{
835+
if (stream->volume == volume)
836+
return;
837+
838+
stream->volume = volume;
839+
updateActiveStreams(out);
840+
}
841+
842+

modules/pins/i2s/audioout.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ class AudioOut @ "xs_audioout_destructor" {
2323
close() @ "xs_audioout_close"
2424
start() @ "xs_audioout_start"
2525
stop() @ "xs_audioout_stop"
26-
enqueue(stream, buffer, repeat, offset, count) @ "xs_audioout_enqueue";
26+
enqueue(stream, kind, buffer, repeat, offset, count) @ "xs_audioout_enqueue";
2727
};
28+
AudioOut.Samples = 1;
29+
AudioOut.Flush = 2;
30+
AudioOut.Callback = 3;
31+
AudioOut.Volume = 4;
2832
Object.freeze(AudioOut.prototype);
2933

3034
export default AudioOut;

0 commit comments

Comments
 (0)