1
/* sqUnixSoundPulseAudio.c -- sound module for Pulse Audio
3
* Author: Derek O'Connell <doc@doconnel.f9.co.uk>
5
* Copyright (C) 2009--2010 by Derek O'Connell
8
* This file is part of Unix Squeak.
10
* Permission is hereby granted, free of charge, to any person obtaining a
11
* copy of this software and associated documentation files (the "Software"),
12
* to deal in the Software without restriction, including without limitation
13
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
* and/or sell copies of the Software, and to permit persons to whom the
15
* Software is furnished to do so, subject to the following conditions:
17
* The above copyright notice and this permission notice shall be included in
18
* all copies or substantial portions of the Software.
20
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
* DEALINGS IN THE SOFTWARE.
28
* Last edited: 2010-04-13 07:45:37 by piumarta on ubuntu
41
#include <sys/errno.h>
51
#include <pulse/simple.h>
52
#include <pulse/error.h>
54
#include <pulse/gccmacro.h>
67
#define snd(expr, what) \
68
if ((rc = snd_##expr) < 0) \
70
fprintf(stderr, "%s: %s\n", what, snd_strerror(rc)); \
76
/* ================================== DEBUGGING */
81
#define DBG_MSG_MAX_LEN 128
83
char *dbg_msg[DBG_MSG_MAX_LEN];
86
printf("DBG: sqUnixSoundMaemo: %s (%d, %s)\n", M, errno, strerror (errno)); \
90
#define DBGERR(M, E) { \
91
sprintf(*dbg_msg, M, E); \
101
/* ================================== TYPES */
105
unsigned long samples;
110
pthread_mutex_t *mutex;
111
pthread_cond_t *cond;
116
/* Left in for debugging >>> */
123
unsigned long maxSamples;
124
unsigned long maxWords;
125
unsigned long maxBytes;
127
audioBuffer_t *buffer;
130
int buffersAllocated;
136
pthread_mutex_t *bufferMutex;
142
gen_sig_t sigStalled;
152
/* PULSE, Simple API parameters */
154
pa_sample_spec pa_spec;
158
/* ================================== FUNCTION PROTOTYPES */
160
static int rate(int rateID);
161
static int rateID(int rate);
163
static inline unsigned short _swapw(unsigned short v); /* From io.h */
165
static int devInputReady(int dev_fd);
167
static void sigWait(gen_sig_t *sig);
168
static void sigReset(gen_sig_t *sig);
169
static void sigSignal(gen_sig_t *sig);
171
static void ioThreadWaitToRun(audioIO_t *audioIO);
172
static void ioThreadExit(audioIO_t *audioIO);
173
static int ioThreadStart(audioIO_t *audioIO);
174
static int ioThreadIsRunning(audioIO_t *audioIO);
175
static void ioThreadStall(audioIO_t *audioIO);
177
static void ioZeroBuffers(audioIO_t *audioIO);
178
static void ioFreeBuffers(audioIO_t *audioIO);
179
static int ioFreeBytes(audioIO_t *audioIO);
180
static int ioIsFull(audioIO_t *audioIO);
181
static int ioAddPlayBuffer(void *buffer, int frameCount);
182
static int ioGetRecordBuffer(void *buffer, int bufferBytes);
183
static int ioAllocBuffers(audioIO_t *audioIO, int frameCount);
184
static int ioGetBufferData(audioIO_t *audioIO, void **buffer, int *frames);
185
static int ioNextBuffer(audioIO_t *audioIO);
187
static void *writerThread(void *ptr);
188
static void *readerThread(void *ptr);
192
/* SQUEAK INTERFACE */
196
static sqInt sound_AvailableSpace(void);
197
static sqInt sound_InsertSamplesFromLeadTime(int frameCount, int srcBufPtr, int samplesOfLeadTime);
198
static sqInt sound_PlaySamplesFromAtLength(int frameCount, int arrayIndex, int startIndex);
199
static sqInt sound_PlaySilence(void);
200
static sqInt sound_Start(int frameCount, int samplesPerSec, int stereo, int semaIndex);
201
static sqInt sound_Stop(void);
203
static sqInt sound_StartRecording(int desiredSamplesPerSec, int stereo, int semaIndex);
204
static sqInt sound_StopRecording(void);
205
static double sound_GetRecordingSampleRate(void);
206
static sqInt sound_RecordSamplesIntoAtLength(int buf, int startSliceIndex, int bufferSizeInBytes);
208
static int mixer_open(char *name);
209
static void mixer_close(void);
210
static inline void mixer_getVolume(char *name, int captureFlag, double *leftLevel, double *rightLevel);
211
static inline void mixer_setVolume(char *name, int captureFlag, double leftLevel, double rightLevel);
212
static int mixer_setSwitch(char *name, int captureFlag, int parameter);
213
static int mixer_getSwitch(char *name, int captureFlag, int channel);
214
static void sound_Volume(double *left, double *right);
215
static void sound_SetVolume(double left, double right);
216
static sqInt sound_SetRecordLevel(sqInt level);
217
static sqInt sound_SetDevice(sqInt id, char *arg);
218
static sqInt sound_GetSwitch(sqInt id, sqInt captureFlag, sqInt channel);
219
static sqInt sound_SetSwitch(sqInt id, sqInt captureFlag, sqInt parameter);
222
/* ==================== */
223
/* ==================== GLOBALS */
224
/* ==================== */
226
/* Left in but not used >>> */
227
#define SQ_SND_PLAY_START_THRESHOLD 7/8
228
#define SQ_SND_PLAY_AVAIL_MIN 4/8
231
/* Arbitrary (apart from minmising latency) >>> */
232
#define MAX_INPUT_BUFFERS 10
233
#define MAX_OUTPUT_BUFFERS 2
236
audioBuffer_t iBuffer[MAX_INPUT_BUFFERS];
237
audioBuffer_t oBuffer[MAX_OUTPUT_BUFFERS];
239
/* STATICALLY INITIALISED SO AUTO-DESTROYED (ON CRASHING FOR INSTANCE) >>> */
243
pthread_mutex_t audioInBufferMutex = PTHREAD_MUTEX_INITIALIZER;
245
pthread_mutex_t audioInRunMutex = PTHREAD_MUTEX_INITIALIZER;
246
pthread_cond_t audioInRunCond = PTHREAD_COND_INITIALIZER;
248
pthread_mutex_t audioInStalledMutex = PTHREAD_MUTEX_INITIALIZER;
249
pthread_cond_t audioInStalledCond = PTHREAD_COND_INITIALIZER;
253
pthread_mutex_t audioOutBufferMutex = PTHREAD_MUTEX_INITIALIZER;
255
pthread_mutex_t audioOutRunMutex = PTHREAD_MUTEX_INITIALIZER;
256
pthread_cond_t audioOutRunCond = PTHREAD_COND_INITIALIZER;
258
pthread_mutex_t audioOutStalledMutex = PTHREAD_MUTEX_INITIALIZER;
259
pthread_cond_t audioOutStalledCond = PTHREAD_COND_INITIALIZER;
264
audioIO_t audioIn, audioOut;
266
int initDone = false;
268
/* EXTRA FOR ALSA BUT UNUSED >>> */
270
static int output_buffer_frames_available = 1;
271
static double max_delay_frames = 0;
276
/* ================================== UTILS */
278
/* RATE CONVERSION: from dsp code but not used (yet). Maybe not needed at all with AlSA */
279
/* RATE CONVERSION: fixed preset rates are used. TBD: choose nearest to requested */
281
static int rate(int rateID) {
282
if (SAMPLE_RATE_8KHZ == rateID) return 8000;
283
if (SAMPLE_RATE_16KHZ == rateID) return 16000;
284
if (SAMPLE_RATE_11_025KHZ == rateID) return 11025;
285
if (SAMPLE_RATE_22_05KHZ == rateID) return 22050;
286
if (SAMPLE_RATE_44_1KHZ == rateID) return 44100;
290
static int rateID(int rate) {
291
if ( 8000 == rate) return SAMPLE_RATE_8KHZ;
292
if ( 8192 == rate) return SAMPLE_RATE_8KHZ;
293
if (16000 == rate) return SAMPLE_RATE_16KHZ;
294
if (11025 == rate) return SAMPLE_RATE_11_025KHZ;
295
if (22050 == rate) return SAMPLE_RATE_22_05KHZ;
296
if (44100 == rate) return SAMPLE_RATE_44_1KHZ;
301
/* From io.h because recorded data has to be Big Endian */
302
static inline unsigned short _swapw(unsigned short v) {
303
return ((v << 8) | (v >> 8));
307
/* Not used but maybe useful */
309
static int devInputReady(int dev_fd) {
313
if (poll (&pfd,1,0)>0) return true;
318
static void printPALatency() {
322
if ((latency = pa_simple_get_latency(audioOut.pa_conn, &error)) == (pa_usec_t) -1)
323
fprintf(stderr, __FILE__": pa_simple_get_latency() failed: %s\n", pa_strerror(error));
325
fprintf(stderr, "%0.0f usec \r", (float)latency);
328
/* ================================== Signal Ops */
330
static void sigWait(gen_sig_t *sig) {
331
pthread_mutex_lock(sig->mutex);
333
pthread_cond_wait(sig->cond, sig->mutex);
335
pthread_mutex_unlock(sig->mutex);
338
static void sigReset(gen_sig_t *sig) {
339
pthread_mutex_lock(sig->mutex);
341
pthread_mutex_unlock(sig->mutex);
344
static void sigSignal(gen_sig_t *sig) {
345
pthread_mutex_lock(sig->mutex);
347
pthread_cond_signal(sig->cond);
348
pthread_mutex_unlock(sig->mutex);
351
/* Here for debugging but direct calls would be ok >>> */
352
static void signalSqueak(audioIO_t *audioIO) {
353
/* printf("@%d",audioIO->sqSemaphore);
355
if (0 < audioIO->sqSemaphore)
356
signalSemaphoreWithIndex(audioIO->sqSemaphore);
361
/* ================================== Thread Ops */
363
static void ioThreadExit(audioIO_t *audioIO) {
364
if (!audioIO->thread) return;
366
sigSignal(&audioIO->sigRun);
367
pthread_join(audioIO->thread, NULL);
371
static int ioThreadStart(audioIO_t *audioIO) {
373
if (audioIO->thread) return true;
374
rc = pthread_create(&audioIO->thread, NULL, audioIO->threadFunc, NULL);
375
if (0 != rc) DBGERR("ioThreadStart(): %d", rc);
379
static int ioThreadIsRunning(audioIO_t *audioIO) {
380
return audioIO->running;
383
static void ioThreadStall(audioIO_t *audioIO) {
384
audioIO->stall = true;
385
sigSignal(&audioIO->sigRun);
386
sigWait(&audioIO->sigStalled);
389
/* Don't attempt to signal Sq here as we may not have a semaphore! */
390
static void ioThreadWaitToRun(audioIO_t *audioIO) {
391
sigSignal(&audioIO->sigStalled);
393
pthread_mutex_lock(audioIO->sigRun.mutex);
394
audioIO->running = false;
396
if (audioIO->stall) audioIO->sigRun.count = 0;
397
audioIO->stall = false;
399
while( !audioIO->sigRun.count )
400
pthread_cond_wait(audioIO->sigRun.cond, audioIO->sigRun.mutex);
401
audioIO->sigRun.count -= 1;
403
audioIO->running = true;
404
pthread_mutex_unlock(audioIO->sigRun.mutex);
406
sigReset(&audioIO->sigStalled);
409
/* ================================== Buffer ops */
411
static void ioZeroBuffers(audioIO_t *audioIO) {
413
for(i=0; i < audioIO->maxBuffers; i++) {
414
audioIO->buffer[i].samples = 0;
415
audioIO->buffer[i].isFree = true;
419
static void ioFreeBuffers(audioIO_t *audioIO) {
421
for(i=0; i < audioIO->maxBuffers; i++) {
422
free(audioIO->buffer[i].buffer);
423
audioIO->buffer[i].buffer = 0;
424
audioIO->buffer[i].samples = 0;
426
audioIO->bufferFree = audioIO->bufferNext = 0;
427
/* audioIO->bufferCount differs for play/record */
430
/* Only used for playing, not for recording */
431
static int ioFreeBytes(audioIO_t *audioIO) {
433
pthread_mutex_lock(audioIO->bufferMutex);
434
freeBytes = audioIO->maxBytes * audioIO->bufferCount;
435
pthread_mutex_unlock(audioIO->bufferMutex);
439
static int ioAllocBuffers(audioIO_t *audioIO, int frameCount) {
442
/* Not preserving buffers when play/record stopped */
443
/* Choosing memory conservation over speed of starting play/record */
445
ioFreeBuffers(audioIO);
446
audioIO->maxSamples = frameCount;
447
audioIO->maxBytes = audioIO->maxSamples * audioIO->bytesPerFrame;
448
audioIO->maxWords = audioIO->maxBytes >> 1;
449
for(i=0; i < audioIO->maxBuffers; i++) {
450
audioIO->buffer[i].buffer = (short *)calloc(audioIO->maxBytes, 1);
451
audioIO->buffer[i].isFree = true;
453
audioIO->buffersAllocated = true;
456
static int ioIsFull(audioIO_t *audioIO) {
457
pthread_mutex_lock(audioIO->bufferMutex);
458
audioIO->bufferFull = (0 < audioIO->buffer[audioIO->bufferFree].samples);
459
pthread_mutex_unlock(audioIO->bufferMutex);
460
return audioIO->bufferFull;
463
/* Could combine some of the following but makes debugging difficult */
465
static int ioAddPlayBuffer(void *buffer, int frameCount) {
467
if (ioIsFull(&audioOut)) return 0;
468
pthread_mutex_lock(audioOut.bufferMutex);
469
bytes = MIN(audioOut.maxBytes, frameCount * audioOut.bytesPerFrame);
470
memcpy(audioOut.buffer[audioOut.bufferFree].buffer, buffer, bytes);
471
audioOut.buffer[audioOut.bufferFree].samples = frameCount;
472
audioOut.buffer[audioOut.bufferFree].isFree = false;
473
audioOut.bufferFree = (audioOut.bufferFree + 1) % audioOut.maxBuffers;
474
audioOut.bufferCount -= 1;
475
pthread_mutex_unlock(audioOut.bufferMutex);
479
static int ioGetRecordBuffer(void *buffer, int bufferBytes) {
480
long samples, sampleBytes;
482
if (bufferBytes <= 0) return 0;
483
/* if (audioIn.buffer[audioIO->bufferNext].samples <=0) return 0;
485
if (audioIn.buffer[audioIn.bufferNext].isFree) return 0;
487
pthread_mutex_lock(audioIn.bufferMutex);
488
samples = audioIn.buffer[audioIn.bufferNext].samples;
489
sampleBytes = MIN(2 * audioIn.pa_spec.channels * samples, bufferBytes);
490
memcpy(buffer, (char *)audioIn.buffer[audioIn.bufferNext].buffer, sampleBytes);
491
/* DMOC 090909 1800: Hmmmm, what if Squeak does not read whole buffer? ATM remaining buffer data lost since */
492
/* ioGetRecordBuffer() frees the buffer after single visit. Needs more work */
493
audioIn.buffer[audioIn.bufferNext].samples = 0;
494
audioIn.buffer[audioIn.bufferNext].isFree = true;
495
audioIn.bufferNext = (audioIn.bufferNext + 1) % audioIn.maxBuffers;
496
audioIn.bufferCount -= 1;
497
pthread_mutex_unlock(audioIn.bufferMutex);
501
static int ioGetBufferData(audioIO_t *audioIO, void **buffer, int *frames) {
502
if (audioIO->buffer[audioIO->bufferNext].isFree) return false;
503
pthread_mutex_lock(audioIO->bufferMutex);
504
*buffer = (void *)(audioIO->buffer[audioIO->bufferNext].buffer);
505
*frames = audioIO->buffer[audioIO->bufferNext].samples;
506
pthread_mutex_unlock(audioIO->bufferMutex);
510
static int ioNextPlayBuffer() {
511
pthread_mutex_lock(audioOut.bufferMutex);
512
audioOut.buffer[audioOut.bufferNext].samples = 0;
513
audioOut.buffer[audioOut.bufferNext].isFree = true;
514
audioOut.bufferNext = (audioOut.bufferNext + 1) % audioOut.maxBuffers;
515
audioOut.stall = (audioOut.bufferNext == audioOut.bufferFree);
516
audioOut.bufferCount += 1;
517
pthread_mutex_unlock(audioOut.bufferMutex);
520
static int ioNextRecordBuffer() {
521
pthread_mutex_lock(audioIn.bufferMutex);
522
audioIn.buffer[audioIn.bufferNext].isFree = false;
523
audioIn.bufferFree = (audioIn.bufferNext + 1) % audioIn.maxBuffers;
524
audioIn.stall = (audioIn.bufferNext == audioIn.bufferFree);
525
audioIn.bufferCount += 1;
526
pthread_mutex_unlock(audioIn.bufferMutex);
529
/* ================================== IO THREADS */
531
static void *writerThread(void *ptr) {
532
struct timespec tm = {0, 1000 * 1000};
534
int nextBuffer, frames;
537
DBGMSG("[writerThread: started]");
542
DBGMSG("[writerThread: waiting]");
544
/* No point signalling squeak *before* running as there may not be a semaphore */
545
ioThreadWaitToRun(&audioOut);
547
if (audioOut.exit) break;
548
if (!audioOut.open || audioOut.stall) continue;
550
DBGMSG("[writerThread: running]");
553
if (!audioOut.open || audioOut.stall || audioOut.exit) break;
555
if (!ioGetBufferData(&audioOut, &buffer, &frames)) {
556
signalSqueak(&audioOut);
560
/*printf("writerThread: buffer: %d, frames %d\n", audioOut.bufferNext, frames);
564
if (!audioOut.open || audioOut.stall || audioOut.exit) break;
565
/* if ((rc = snd_pcm_writei(audioOut.alsaHandle, buffer, frames)) < frames) {
568
/* PA: Have to assume for now that all frames were written */
569
if (pa_simple_write(audioOut.pa_conn, buffer, (size_t) (frames * audioOut.bytesPerFrame), &rc) < 0) {
570
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(rc));
571
/* printf("writerThread: sent %d, actual %d\n", frames, rc);
576
/* PA: Have to assume for now that all frames were written */
577
/* buffer = (short *)((char *)buffer + rc * audioOut.bytesPerFrame);
580
/* *** SO FOLLOWING CODE *AND* THE ENCLOSING WHILE-LOOP REDUNDANT!!! (so just break out of loop) *** */
581
/* buffer = (short *)((char *)buffer + frames * audioOut.bytesPerFrame);
587
if (!audioOut.open || audioOut.stall || audioOut.exit) break;
589
if (!audioOut.open || audioOut.stall || audioOut.exit) break;
591
signalSqueak(&audioOut);
594
if (audioOut.exit) break;
597
DBGMSG("[writerThread: stopped]");
602
static void *readerThread(void *ptr) {
607
DBGMSG("[readerThread: started]");
612
DBGMSG("[readerThread: waiting]");
614
ioThreadWaitToRun(&audioIn);
616
if (audioIn.exit) break;
617
if (!audioIn.open || audioIn.stall) continue;
619
DBGMSG("[readerThread: running]");
622
if (!audioIn.open || audioIn.stall || audioIn.exit) break;
624
/* NB: PA Simple API does not return number of bytes/samples recorded */
625
/* (so have to assume full buffer everytime (poss padded if less than requested) */
627
/* rc = snd_pcm_readi(audioIn.alsaHandle, audioIn.alsaBuffer, audioIn.alsaFrames);
629
if (pa_simple_read(audioIn.pa_conn, (char *)(audioIn.buffer[audioIn.bufferFree].buffer), audioIn.maxBytes, &rc) < 0) {
630
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(rc));
634
if (!audioIn.open || audioIn.stall || audioIn.exit) break;
636
/* PA: Assume max buffer frames returned... */
637
rc = audioIn.maxSamples;
639
/* EPIPE means overrun */
642
printf("readerThread: overrun occurred\n");
643
snd_pcm_prepare(audioIn.alsaHandle);
648
printf("readerThread: error from read: %s\n", snd_strerror(rc));
652
if (rc != (int)audioIn.alsaFrames) {
653
printf("readerThread: short read, read %d frames\n", rc);
657
if (!audioIn.buffer[audioIn.bufferFree].isFree) {
658
printf("readerThread: No free buffers!\n");
662
if (!audioIn.open || audioIn.stall || audioIn.exit) break;
664
/* PA: Complete buffer should be returned so skipping check and moving guts to end of loop */
666
if ((audioIn.buffer[audioIn.bufferFree].samples + rc) > audioIn.maxSamples) {
667
ioNextRecordBuffer();
668
if (!audioIn.open || audioIn.stall || audioIn.exit) break;
669
signalSqueak(&audioIn);
673
/* Next won't get called anyway because of "stall" in ioNextRecordBuffer() */
674
/* Left here in case needed for debugging */
676
if (!audioIn.buffer[audioIn.bufferFree].isFree) {
677
printf("readerThread: No free buffers!\n");
682
/* PA: Endian swap may not be needed... */
684
/* Endian Swap (rc = frames = Word Count in this case) */
686
p = (unsigned short *)(audioIn.buffer[audioIn.bufferFree].buffer);
692
/* PA: No copy required since record buffer used directly... */
694
memcpy((char *)(audioIn.buffer[audioIn.bufferFree].buffer) + 2 * audioIn.buffer[audioIn.bufferFree].samples, audioIn.alsaBuffer, rc*2);
695
audioIn.buffer[audioIn.bufferFree].samples += rc;
698
/* PA: No indication of actual bytes/samples read so assuming full buffer read (or padded)... */
699
audioIn.buffer[audioIn.bufferFree].samples = audioIn.maxSamples;
701
if (!audioIn.open || audioIn.stall || audioIn.exit) break;
703
/* PA: These three lines moved from above (since assuming full buffers are read every time) */
704
ioNextRecordBuffer();
705
if (!audioIn.open || audioIn.stall || audioIn.exit) break;
706
signalSqueak(&audioIn);
709
if (audioIn.exit) break;
711
DBGMSG("[readerThread: stopped]");
715
/* ================================== IO INIT */
717
static int ioInit() {
718
if (initDone) return true;
724
audioOut.dbgName = "play";
725
audioOut.device = "pa-simple";
728
audioOut.open = false;
730
audioOut.maxSamples = 0;
731
audioOut.maxWords = 0;
732
audioOut.maxBytes = 0;
734
audioOut.maxBuffers = MAX_OUTPUT_BUFFERS;
735
audioOut.buffer = oBuffer;
736
audioOut.bufferFree = 0;
737
audioOut.bufferNext = 0;
738
audioOut.bufferCount = audioOut.maxBuffers;
739
audioOut.bufferFull = 0;
740
audioOut.bufferMutex = &audioOutBufferMutex;
741
audioOut.buffersAllocated = false;
743
audioOut.threadFunc = writerThread;
746
audioOut.sigRun.mutex = &audioOutRunMutex;
747
audioOut.sigRun.cond = &audioOutRunCond;
748
sigReset(&audioOut.sigRun);
750
audioOut.sigStalled.mutex = &audioOutStalledMutex;
751
audioOut.sigStalled.cond = &audioOutStalledCond;
752
sigReset(&audioOut.sigStalled);
754
audioOut.running = 0;
758
audioOut.sqSemaphore = 0;
761
audioOut.bytesPerFrame = 4; /* Stereo S16LE */
763
audioOut.pa_conn = null;
765
ioThreadStart(&audioOut);
769
audioIn.dbgName = "rec";
772
audioIn.device = "pa-simple";
775
audioIn.open = false;
777
audioIn.maxSamples = 0;
778
audioIn.maxWords = 0;
779
audioIn.maxBytes = 0;
781
audioIn.maxBuffers = MAX_INPUT_BUFFERS;
782
audioIn.buffer = iBuffer;
783
audioIn.bufferFree = 0;
784
audioIn.bufferNext = 0;
785
audioIn.bufferCount = 0; /* No buffers yet. Was audioIn.maxBuffers; */
786
audioIn.bufferFull = 0;
787
audioIn.bufferMutex = &audioInBufferMutex;
788
audioIn.buffersAllocated = false;
790
audioIn.threadFunc= readerThread;
793
audioIn.sigRun.mutex = &audioInRunMutex;
794
audioIn.sigRun.cond = &audioInRunCond;
795
sigReset(&audioIn.sigRun);
797
audioIn.sigStalled.mutex = &audioInStalledMutex;
798
audioIn.sigStalled.cond = &audioInStalledCond;
799
sigReset(&audioIn.sigStalled);
805
audioIn.sqSemaphore = 0;
808
audioIn.bytesPerFrame = 2; /* Mono S16LE */
810
audioIn.pa_conn = null;
812
ioThreadStart(&audioIn);
815
/* ============================================ */
816
/* ================================== VM PLUGIN */
817
/* ============================================ */
822
/* ================================== AUDIO OUT */
824
static sqInt sound_AvailableSpace(void) {
825
return ioFreeBytes(&audioOut);
828
static sqInt sound_InsertSamplesFromLeadTime(int frameCount, int srcBufPtr, int samplesOfLeadTime) {
829
DBGMSG(">sound_InsertSamplesFromLeadTime()");
830
return 0; /* or maxBytes? */
834
static sqInt sound_PlaySamplesFromAtLength(int frameCount, int arrayIndex, int startIndex) {
835
unsigned int bufferNext, samples, sampleBytes;
837
if (0 >= frameCount) return 0;
839
samples = MIN(audioOut.maxSamples, frameCount);
841
if (0 == (sampleBytes = ioAddPlayBuffer((void *)(arrayIndex + startIndex * 2 * audioOut.pa_spec.channels), samples)))
842
DBGMSG("sound_PlaySamplesFromAtLength(): No free buffers!");
844
sigSignal(&audioOut.sigRun);
849
static sqInt sound_PlaySilence(void) {
850
DBGMSG(">sound_PlaySilence()");
851
ioThreadStall(&audioOut);
852
return 0; /* or maxBytes? */
856
static sqInt sound_Start(int frameCount, int samplesPerSec, int stereo, int semaIndex) {
859
DBGMSG(">sound_Start()");
862
printf("\tframeCount: %d, samplesPerSec: %d, stereo: %d, semaIndex: %d\n", frameCount, samplesPerSec, stereo, semaIndex);
865
if (audioOut.open) return true;
867
audioOut.pa_spec.format = PA_SAMPLE_S16LE;
868
audioOut.pa_spec.rate = samplesPerSec; /* rate(SAMPLE_RATE_22_05KHZ) for Squeak */
869
audioOut.pa_spec.channels = stereo ? 2 : 1;
870
audioOut.pa_conn = NULL;
872
/* Create a new playback stream */
873
if (!(audioOut.pa_conn = pa_simple_new(NULL, "Scratch", PA_STREAM_PLAYBACK, NULL, "playback", &audioOut.pa_spec, NULL, NULL, &rc))) {
874
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(rc));
879
ioAllocBuffers(&audioOut, frameCount);
880
audioOut.bufferCount = audioOut.maxBuffers; /* Has to be reset everytime */
882
audioOut.sqSemaphore = semaIndex;
884
audioOut.open = true;
886
sigSignal(&audioOut.sigRun);
888
/* error possibly left over from dsp-protocol.c code */
889
/* dsp-protocol.c in current ALSA not capturing EINTR/EAGAIN */
890
/* EINTR/EAGAIN from dsp-protocol.c not raised up to ALSA so not caught by ALSA */
891
/* Clearing errno here to see if Squeak can continue regardless */
894
DBGMSG("<sound_Start()");
899
static sqInt sound_Stop(void) {
902
DBGMSG(">sound_Stop()");
904
if (!audioOut.open) return true;
905
audioOut.open = false;
907
if (NULL == audioOut.pa_conn) return true;
909
ioThreadStall(&audioOut);
911
if (pa_simple_drain(audioOut.pa_conn, &rc) < 0) {
912
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(rc));
915
if (NULL != audioOut.pa_conn)
916
pa_simple_free(audioOut.pa_conn);
918
ioFreeBuffers(&audioOut);
920
audioOut.pa_conn = NULL;
921
audioOut.sqSemaphore = 0;
923
DBGMSG("<sound_Stop()");
929
/* ================================== AUDIO IN */
931
static sqInt sound_StartRecording(int desiredSamplesPerSec, int stereo, int semaIndex) {
933
pa_buffer_attr pa_buffer_metrics; /* For recording */
935
DBGMSG(">sound_StartRecording()");
937
if (audioIn.open) return true;
939
audioIn.pa_spec.format = PA_SAMPLE_S16LE;
940
audioIn.pa_spec.rate = desiredSamplesPerSec;
941
audioIn.pa_spec.channels = stereo ? 2 : 1;
942
audioIn.pa_conn = NULL;
944
pa_buffer_metrics.maxlength = (uint32_t) -1;
945
pa_buffer_metrics.tlength = (uint32_t) -1; /* playback only */
946
pa_buffer_metrics.prebuf = (uint32_t) -1; /* playback only */
947
pa_buffer_metrics.minreq = (uint32_t) -1; /* playback only */
948
pa_buffer_metrics.fragsize = pa_usec_to_bytes(20*1000, &audioIn.pa_spec);
950
/* Create the recording stream */
951
if (!(audioIn.pa_conn = pa_simple_new( NULL,
961
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(rc));
966
/* Only rate supported on the N810 (atm) is 8000 */
968
audioIn.alsaRate = 8000;
971
/* 20Hz update freq for Squeak sounds reasonable, so... */
972
audioIn.maxSamples = audioIn.pa_spec.rate / 20;
974
/* Use a buffer large enough to hold one period (assuming 2 bytes/sample) */
976
audioIn.alsaBufferSize = audioIn.maxSamples * 2 * audioIn.pa_spec.channels;
977
audioIn.alsaBuffer = (char *) malloc(audioIn.alsaBufferSize);
980
/* Buffers will be filled before signalling Squeak. So rate & buffer size determined signalling freq... */
981
ioAllocBuffers(&audioIn, audioIn.pa_spec.rate / 20 ); /* for Sq signalling rate of 20Hz */
982
audioIn.bufferCount = 0; /* Has to be reset everytime */
984
audioIn.sqSemaphore = semaIndex;
988
sigSignal(&audioIn.sigRun);
990
DBGMSG("<sound_StartRecording()");
994
static sqInt sound_StopRecording(void) {
995
DBGMSG(">sound_StopRecording()");
997
if (!audioIn.open) return;
998
audioIn.open = false;
1000
if (NULL == audioIn.pa_conn) return;
1002
ioThreadStall(&audioIn);
1004
pa_simple_free(audioIn.pa_conn);
1006
ioFreeBuffers(&audioIn);
1008
audioIn.pa_conn = NULL;
1010
audioIn.sqSemaphore = 0;
1012
DBGMSG("<sound_StopRecording()");
1016
static double sound_GetRecordingSampleRate(void) {
1017
return (double)audioIn.pa_spec.rate;
1020
static sqInt sound_RecordSamplesIntoAtLength(int buf, int startSliceIndex, int bufferSizeInBytes) {
1021
unsigned int bufferNext, bufferBytes, sampleBytes;
1023
bufferBytes = MAX(0, bufferSizeInBytes - (startSliceIndex * 2));
1024
if (0 == bufferBytes) {
1025
printf("***(%d) sound_RecordSamplesIntoAtLength(): No space in Squeak buffer!\n", startSliceIndex);
1029
/* DMOC 090909 1800: Hmmmm, what if Squeak does not read whole buffer? ATM remaining buffer data lost since */
1030
/* ioGetRecordBuffer() frees the buffer after single visit. Needs more work */
1032
bufferNext = audioIn.bufferNext; /* preserved for debug output */
1033
sampleBytes = ioGetRecordBuffer((void *)(buf + (startSliceIndex * 2)), bufferBytes);
1035
if (0 < sampleBytes)
1036
printf(" sound_RecordSamplesIntoAtLength(%d, %d, %d) %d, %d\n", buf, startSliceIndex, bufferSizeInBytes, bufferNext, sampleBytes);
1038
printf("***sound_RecordSamplesIntoAtLength(%d, %d, %d) %d, %d\n", buf, startSliceIndex, bufferSizeInBytes, bufferNext, sampleBytes);
1040
return MAX(0, sampleBytes)/(2 * audioIn.pa_spec.channels);
1044
/* ================================== sound mixer */
1047
static int sound_nomixer = 0;
1048
static snd_mixer_t *mixer_handle = 0;
1049
static snd_mixer_elem_t *mixer_element = 0;
1052
static int mixer_open(char *name) {
1057
static void mixer_close(void) {
1061
static inline void mixer_getVolume(char *name, int captureFlag, double *leftLevel, double *rightLevel) {
1065
static inline void mixer_setVolume(char *name, int captureFlag, double leftLevel, double rightLevel) {
1069
static int mixer_setSwitch(char *name, int captureFlag, int parameter) {
1074
static int mixer_getSwitch(char *name, int captureFlag, int channel) {
1079
static void sound_Volume(double *left, double *right) {
1085
static void sound_SetVolume(double left, double right) {
1089
static sqInt sound_SetRecordLevel(sqInt level) {
1095
static sqInt sound_SetDevice(sqInt id, char *arg) {
1100
static sqInt sound_GetSwitch(sqInt id, sqInt captureFlag, sqInt channel) {
1105
static sqInt sound_SetSwitch(sqInt id, sqInt captureFlag, sqInt parameter) {
1113
#include "SqSound.h"
1117
#include "SqModule.h"
1119
static void sound_parseEnvironment(void) {
1123
sound_SetDevice(0, NULL);
1124
sound_SetDevice(1, NULL);
1125
sound_SetDevice(2, NULL);
1127
if ( getenv("SQUEAK_NOMIXER" )) sound_nomixer= 1;
1128
if ((ev= getenv("SQUEAK_SOUNDCARD"))) sound_SetDevice(0, ev);
1129
if ((ev= getenv("SQUEAK_PLAYBACK" ))) sound_SetDevice(1, ev);
1130
if ((ev= getenv("SQUEAK_CAPTURE" ))) sound_SetDevice(2, ev);
1134
static int sound_parseArgument(int argc, char **argv) {
1136
if (!strcmp(argv[0], "-nomixer" )) { sound_nomixer= 1; return 1; }
1139
if (!strcmp(argv[0], "-soundcard")) { sound_SetDevice(0, argv[1]); return 2; }
1140
if (!strcmp(argv[0], "-playback" )) { sound_SetDevice(1, argv[1]); return 2; }
1141
if (!strcmp(argv[0], "-capture" )) { sound_SetDevice(2, argv[1]); return 2; }
1147
static void sound_printUsage(void) {
1148
printf("\nPulseAudio <option>s: <none>\n");
1150
printf(" -nomixer disable mixer (volume) adjustment\n");
1151
printf(" -soundcard <name> open the named sound card (default: %s)\n", sound_device);
1152
printf(" -playback <name> play to the named sound device (default: %s)\n", sound_playback);
1153
printf(" -capture <name> record from the named sound device (default: %s)\n", sound_capture);
1157
static void sound_printUsageNotes(void) {
1160
static void *sound_makeInterface(void) {
1162
// sigalrm_save(); // DMOC: Being here assumes old handler same for run duration! Same for sigio handler.
1164
// DMOC: Rethink: Signal captured once, preserved and restored when/where necessary?
1169
#ifdef USE_RESOURCE_MANAGER
1170
printf("USE_RESOURCE_MANAGER\n");
1175
return &sound_PA_itf;
1178
SqModuleDefine(sound, pulse);