7878
7979typedef 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
8686typedef 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
132134static void audioMix (modAudioOut out , int samplesToGenerate , OUTPUTSAMPLETYPE * output );
133135static void endOfElement (modAudioOut out , modAudioOutStream stream );
136+ static void setStreamVolume (modAudioOut out , modAudioOutStream stream , int volume );
134137
135138void 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+
289302void 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+
0 commit comments