Example #1
0
void MpPortAudioMixer::getMixerName(UtlString& name, int i) const
{
   OsLock lock(MpPortAudioDriver::ms_driverMutex);

   if (m_pxMixer)
   {
      name = Px_GetMixerName(m_pxMixer, i);
   }
   else
   {
      name = NULL;
   }
}
Example #2
0
int main(int argc, char **argv)
{
   int num_mixers;
   int i;
   PaError error;
   PortAudioStream *stream;
   int recDeviceNum;
   int playDeviceNum;
   int inputChannels;
   int outputChannels;
   int num_devices;
   int device;
   int opt;
   int opts=-1, optm=0;
   float optv=-2, opto=-2, opti=-2, opth=-2, optb=-2;
   
   printf("px_test: a program to demonstrate the capabilities of PortMixer\n");
   printf("By Dominic Mazzoni\n");
   printf("\n");
   printf("Usage:\n");
   printf("  -d [device number]\n");
   printf("  -m [mixer number]\n");
   printf("  -v [vol] (Master volume)\n");
   printf("  -o [vol] (PCM output volume)\n");
   printf("  -i [vol] (Input volume)\n");
   printf("  -s [source number] (Input source)\n");
   printf("  -h [vol] (Playthrough)\n");
   printf("  -b [bal] (Balance: -1.0....1.0)\n");
   printf("\n");
   printf("All volumes are between 0.0 and 1.0.\n");
   printf("\n");

   error = Pa_Initialize();
   if (error != 0) {
      printf("PortAudio error: %s\n", Pa_GetErrorText(error));
      return -1;
   }

   num_devices = Pa_CountDevices();

   device = Pa_GetDefaultInputDeviceID();
   recDeviceNum = paNoDevice;
   playDeviceNum = paNoDevice;
   inputChannels = 0;
   outputChannels = 0;

   while(-1 != (opt=getopt(argc, argv, "d:m:v:o:i:s:h:b:"))) {
      switch(opt) {
      case 'd':
         device = atoi(optarg);
         printf("Set device to %d\n", device);
         break;
      case 'm':
         optm = atoi(optarg);
         printf("Set mixer number to %d\n", optm);
         break;
      case 'v':
         optv = getvolarg(optarg); break;
      case 'o':
         opto = getvolarg(optarg); break;
      case 'i':
         opti = getvolarg(optarg); break;
      case 'h':
         opth = getvolarg(optarg); break;
      case 'b':
         optb = atof(optarg); break;
      case 's':
         opts = atoi(optarg); break;
      }
   }

   printf("Devices:\n");
   for(i=0; i<num_devices; i++) {
      const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(i);
      if (i==device) {
         printf("* ");
         if (deviceInfo->maxInputChannels > 0) {
            recDeviceNum = device;
            inputChannels = deviceInfo->maxInputChannels;
         }
         if (deviceInfo->maxOutputChannels > 0) {
            playDeviceNum = device;
            outputChannels = deviceInfo->maxOutputChannels;
         }
      }
      else
         printf("  ");
      printf("Device %d: %s in=%d out=%d",
             i, deviceInfo->name,
             deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels);
      if (i == Pa_GetDefaultInputDeviceID())
         printf(" (default input)");
      if (i == Pa_GetDefaultOutputDeviceID())
         printf(" (default output)");
      printf("\n");
   }
   printf("\n");
   
   error = Pa_OpenStream(&stream, recDeviceNum, inputChannels, paFloat32, NULL,
                         playDeviceNum, outputChannels, paFloat32, NULL,
                         44101, 512, 1, paClipOff | paDitherOff,
                         DummyCallbackFunc, NULL);

   if (error) {
      printf("PortAudio error %d: %s\n", error,
             Pa_GetErrorText(error));
      return -1;
   }
   
   num_mixers = Px_GetNumMixers(stream);
   printf("Number of mixers for device %d: %d\n", device, num_mixers);
   for(i=0; i<num_mixers; i++) {
      PxMixer *mixer;
      int num;
      int j;

      printf("Mixer %d: %s\n", i, Px_GetMixerName(stream, i));
      mixer = Px_OpenMixer(stream, i);
      if (!mixer) {
         printf("  Could not open mixer!\n");
         continue;
      }

      if (i == optm) {
         if (optv!=-2) {
            Px_SetMasterVolume(mixer, optv);
            printf("  Set master volume\n");
         }
         if (opto!=-2) {
            Px_SetPCMOutputVolume(mixer, opto);
            printf("  Set output volume\n");
         }
         if (opti!=-2) {
            Px_SetInputVolume(mixer, opti);
            printf("  Set input volume\n");
         }
         if (opth!=-2) {
            Px_SetPlaythrough(mixer, opth);
            printf("  Set playthrough volume\n");
         }
         if (opts!=-2) {
            Px_SetCurrentInputSource(mixer, opts);
            printf("  Set input source\n");
         }
         if (optb!=-2) {
            Px_SetOutputBalance(mixer, optb);
            printf("  Set balance\n");
         }
      }
      
      printf("  Master volume: %.2f\n", Px_GetMasterVolume(mixer));
      printf("  PCM output volume: %.2f\n", Px_GetPCMOutputVolume(mixer));

      num = Px_GetNumOutputVolumes(mixer);
      printf("  Num outputs: %d\n", num);
      for(j=0; j<num; j++) {
         printf("    Output %d (%s): %.2f\n",
                j,
                Px_GetOutputVolumeName(mixer, j),
                Px_GetOutputVolume(mixer, j));
      }

      num = Px_GetNumInputSources(mixer);
      printf("  Num input sources: %d\n", num);
      for(j=0; j<num; j++) {
         printf("    Input %d (%s) %s\n",
                j,
                Px_GetInputSourceName(mixer, j),
                (Px_GetCurrentInputSource(mixer)==j?
                 "SELECTED": ""));
      }
      printf("  Input volume: %.2f\n", Px_GetInputVolume(mixer));

      printf("  Playthrough:");
      if (Px_SupportsPlaythrough(mixer))
         printf(" %.2f\n", Px_GetPlaythrough(mixer));
      else
         printf(" not supported.\n");

      printf("  Output balance:");
      if (Px_SupportsOutputBalance(mixer))
         printf(" %.2f\n", Px_GetOutputBalance(mixer));
      else
         printf(" not supported.\n");

      Px_CloseMixer(mixer);
   }

   Pa_CloseStream(stream);

   Pa_Terminate();

   return 0;
}
Example #3
0
static void *output_thread(void *ptr) {
	int err;
	int output_buffer_size;
#ifndef PORTAUDIO_DEV
	int num_mixers, nbVolumes, volumeIdx;
#endif
	struct timeval  now;
	struct timespec timeout;
	
	slimaudio_t *audio = (slimaudio_t *) ptr;
	audio->output_STMs = false;
	audio->output_STMu = false;

        err = Pa_Initialize();
        if (err != paNoError) {
                printf("PortAudio error4: %s Could not open any audio devices.\n", Pa_GetErrorText(err) );
                exit(-1);
        }

	if ( audio->renice )
		if ( slimproto_renice_thread (-5) ) /* Increase priority */
			fprintf(stderr, "output_thread: renice failed. Got Root?\n");

#ifndef PORTAUDIO_DEV
	DEBUGF("output_thread: output_device_id  : %i\n", audio->output_device_id );
	DEBUGF("output_thread: pa_framesPerBuffer: %lu\n", pa_framesPerBuffer );
	DEBUGF("output_thread: pa_numberOfBuffers: %lu\n", pa_numberOfBuffers );

	err = Pa_OpenStream(	&audio->pa_stream,	/* stream */
				paNoDevice,		/* input device */
				0,			/* input channels */
				0,			/* input sample format */
				NULL,			/* input driver info */
				audio->output_device_id,/* output device */
				2,			/* output channels */
				paInt16,		/* output sample format */
				NULL,			/* output driver info */
				44100.0,		/* sample rate */
				pa_framesPerBuffer,	/* frames per buffer */
				pa_numberOfBuffers,	/* number of buffers */
				paNoFlag,		/* stream flags */
				pa_callback,		/* callback */
				audio);			/* user data */
#else
	PaStreamParameters outputParameters;
	const PaDeviceInfo * paDeviceInfo;
	float newLatency;

#ifdef PADEV_WASAPI
	PaWasapiStreamInfo streamInfo;
	const PaHostApiInfo *paHostApiInfo;
#endif

	paDeviceInfo = Pa_GetDeviceInfo(audio->output_device_id);
	/* Device is not stereo or better, abort */
	if (paDeviceInfo->maxOutputChannels < 2)
	{
		printf("output_thread: PortAudio device does not support 44.1KHz, 16-bit, stereo audio.\n");
		printf("output_thread: Use -L for a list of supported audio devices, then use -o followed\n");
		printf("output_thread: by the device number listed before the colon.  See -h for details.\n");
		exit(-2);
	}
	outputParameters.device = audio->output_device_id;
#ifdef SLIMPROTO_ZONES
	outputParameters.channelCount = 2 * audio->output_num_zones;
#else
	outputParameters.channelCount = 2;
#endif
	outputParameters.sampleFormat = paInt16;
	outputParameters.suggestedLatency = paDeviceInfo->defaultHighOutputLatency;

	if ( audio->modify_latency )
	{
		newLatency = (float) audio->user_latency / 1000.0;
		if ( ( newLatency > 1.0 ) || ( newLatency <= paDeviceInfo->defaultLowOutputLatency ) )
		{
			fprintf (stderr, "User defined latency %f out of range %f-1.0, using default.\n",
					newLatency, paDeviceInfo->defaultLowOutputLatency );
			newLatency = paDeviceInfo->defaultHighOutputLatency;
		}
		outputParameters.suggestedLatency = newLatency ;
	}

#ifdef PADEV_WASAPI
	/* Use exclusive mode for WASAPI device, default is shared */

	paHostApiInfo = Pa_GetHostApiInfo ( paDeviceInfo->hostApi );
	if ( paHostApiInfo != NULL )
	{
		if ( paHostApiInfo->type == paWASAPI )
		{
			/* Use exclusive mode for WasApi device, default is shared */
			if (wasapi_exclusive)
			{
				streamInfo.size = sizeof(PaWasapiStreamInfo);
				streamInfo.hostApiType = paWASAPI;
				streamInfo.version = 1;
				streamInfo.flags = paWinWasapiExclusive;
				outputParameters.hostApiSpecificStreamInfo = &streamInfo;

				DEBUGF("WASAPI: Exclusive\n");
			}
			else
			{
				outputParameters.hostApiSpecificStreamInfo = NULL;

				DEBUGF("WASAPI: Shared\n");
			}
		}
	}
#else
	outputParameters.hostApiSpecificStreamInfo = NULL;
#endif

	DEBUGF("paDeviceInfo->deviceid %d\n", outputParameters.device);
	DEBUGF("paDeviceInfo->maxOutputChannels %i\n", paDeviceInfo->maxOutputChannels);
	DEBUGF("outputParameters.suggestedLatency %f\n", outputParameters.suggestedLatency);
	DEBUGF("paDeviceInfo->defaultHighOutputLatency %f\n", (float) paDeviceInfo->defaultHighOutputLatency);
	DEBUGF("paDeviceInfo->defaultLowOutputLatency %f\n", (float) paDeviceInfo->defaultLowOutputLatency);
	DEBUGF("paDeviceInfo->defaultSampleRate %f\n", paDeviceInfo->defaultSampleRate);

	err = Pa_OpenStream (	&audio->pa_stream,				/* stream */
				NULL,						/* inputParameters */
				&outputParameters,				/* outputParameters */
				44100.0,					/* sample rate */
				paFramesPerBufferUnspecified,			/* framesPerBuffer */
				paPrimeOutputBuffersUsingStreamCallback,	/* streamFlags */
				pa_callback,					/* streamCallback */
				audio);						/* userData */
#endif

#ifdef BSD_THREAD_LOCKING
	pthread_mutex_lock(&audio->output_mutex);
#endif

	if (err != paNoError) {
		printf("output_thread: PortAudio error1: %s\n", Pa_GetErrorText(err) );	
		exit(-1);
	}

#ifndef PORTAUDIO_DEV
	num_mixers = Px_GetNumMixers(audio->pa_stream);
	while (--num_mixers >= 0) {
		DEBUGF("Mixer: %s\n", Px_GetMixerName(audio->pa_stream, num_mixers));
	}
	
	if (audio->volume_control == VOLUME_DRIVER) {
		DEBUGF("Opening mixer.\n" );
		audio->px_mixer = Px_OpenMixer(audio->pa_stream, 0);
	}

	if (audio->px_mixer != NULL) {
		DEBUGF("Px_mixer = %p\n", audio->px_mixer);
		DEBUGF("PCM volume supported: %d.\n", 
		       Px_SupportsPCMOutputVolume(audio->px_mixer));

		nbVolumes = Px_GetNumOutputVolumes(audio->px_mixer);
		DEBUGF("Nb volumes supported: %d.\n", nbVolumes);
		for (volumeIdx=0; volumeIdx<nbVolumes; ++volumeIdx) {
			DEBUGF("Volume %d: %s\n", volumeIdx, 
				Px_GetOutputVolumeName(audio->px_mixer, volumeIdx));
		}
	}
#endif

	while (audio->output_state != QUIT) {

		switch (audio->output_state) {
			case STOPPED:
				audio->decode_num_tracks_started = 0L;
				audio->stream_samples = 0UL;
				audio->pa_streamtime_offset = audio->stream_samples;

				DEBUGF("output_thread STOPPED: %llu\n",audio->pa_streamtime_offset);

				slimaudio_buffer_set_readopt(audio->output_buffer, BUFFER_BLOCKING);

			case PAUSED:
				/* We report ourselves to the server every few seconds
				** as a keep-alive.  This is required for Squeezebox Server
				** v6.5.x although technically, "stat" is not a valid event 
				** code for the STAT Client->Server message.  This was 
				** lifted by observing how a Squeezebox3 reports itself to 
				** the server using Squeezebox Server's d_slimproto and 
				** d_slimproto_v tracing services.  Note that Squeezebox3
				** seems to report every 1 second or so, but the server only
				** drops the connection after 15-20 seconds of inactivity.
				*/
				DEBUGF("output_thread PAUSED: %llu\n",audio->pa_streamtime_offset);

			  	if (audio->keepalive_interval <= 0) {
					pthread_cond_wait(&audio->output_cond, &audio->output_mutex);
				}
				else {
					gettimeofday(&now, NULL);
					timeout.tv_sec = now.tv_sec + audio->keepalive_interval;
					timeout.tv_nsec = now.tv_usec * 1000;				
					err = pthread_cond_timedwait(&audio->output_cond,
								     &audio->output_mutex, &timeout);
					if (err == ETIMEDOUT) {
						DEBUGF("Sending keepalive. Interval=%ds.\n", audio->keepalive_interval);
						output_thread_stat(audio, "stat");
					}
				}

				break;

			case PLAY:
				audio->output_predelay_frames = audio->output_predelay_msec * 44.100;

				DEBUGF("output_thread PLAY: output_predelay_frames: %i\n",
					audio->output_predelay_frames);

				output_buffer_size = slimaudio_buffer_available(audio->output_buffer);

				DEBUGF("output_thread BUFFERING: output_buffer_size: %i output_threshold: %i",
					output_buffer_size, audio->output_threshold);
				DEBUGF(" buffering_timeout: %i\n", audio->buffering_timeout);

				if ( (output_buffer_size < audio->output_threshold) && (audio->buffering_timeout > 0) )
				{
					pthread_mutex_unlock(&audio->output_mutex);
					pthread_cond_broadcast(&audio->output_cond);

					Pa_Sleep(100);

					pthread_mutex_lock(&audio->output_mutex);
					audio->buffering_timeout--;
				}
				else
				{
					DEBUGF("output_thread PLAY: start stream: %llu\n",
						audio->pa_streamtime_offset);

					audio->buffering_timeout = BUFFERING_TIMEOUT;

					err = Pa_StartStream(audio->pa_stream);
					if (err != paNoError)
					{
						printf("output_thread: PortAudio error2: %s\n", Pa_GetErrorText(err));
						exit(-1);
					}

					audio->output_state = PLAYING;

					pthread_cond_broadcast(&audio->output_cond);
				}

				break;

			case BUFFERING:
				DEBUGF("output_thread BUFFERING: %llu\n",audio->pa_streamtime_offset);

			case PLAYING:			
				gettimeofday(&now, NULL);
				timeout.tv_sec = now.tv_sec + 1;
				timeout.tv_nsec = now.tv_usec * 1000;
				err = pthread_cond_timedwait(&audio->output_cond, &audio->output_mutex, &timeout);

				if (err == ETIMEDOUT)
				{
					DEBUGF("output_thread ETIMEDOUT-PLAYING: %llu\n",audio->pa_streamtime_offset);
					output_thread_stat(audio, "STMt");
				}

				/* Track started */				
				if (audio->output_STMs)
				{
					audio->output_STMs = false;
					audio->decode_num_tracks_started++;

					audio->replay_gain = audio->start_replay_gain;
					slimaudio_output_vol_adjust(audio);

					audio->pa_streamtime_offset = audio->stream_samples;

					DEBUGF("output_thread STMs-PLAYING: %llu\n",audio->pa_streamtime_offset);
					output_thread_stat(audio, "STMs");
				}

				/* Data underrun
				** On buffer underrun causes the server to switch to the next track.
				*/
				if (audio->output_STMu)
				{
					audio->output_STMu = false;

					audio->output_state = STOP;

					DEBUGF("output_thread STMu-PLAYING: %llu\n",audio->pa_streamtime_offset);
					output_thread_stat(audio, "STMu");

					pthread_cond_broadcast(&audio->output_cond);
				}

				break;
		
			case STOP:
#ifndef PORTAUDIO_DEV
				if ( (err = Pa_StreamActive(audio->pa_stream) ) > 0)
				{
					err = Pa_StopStream(audio->pa_stream);
					if (err != paNoError)
					{
						printf("output_thread: PortAudio error3: %s\n", Pa_GetErrorText(err) );	
						exit(-1);
					}
				}
				else
				{
					if ( err != paNoError)
					{
						printf("output_thread: PortAudio error9: %s\n",
						Pa_GetErrorText(err) );
						exit(-1);
					}
				}
#else
				if ( (err = Pa_IsStreamActive(audio->pa_stream)) > 0) {
					err = Pa_StopStream(audio->pa_stream);
					if (err != paNoError) {
						printf("output_thread[STOP]: PortAudio error3: %s\n",
									Pa_GetErrorText(err) );
						exit(-1);
					}
				} else if ( err != paNoError) {
					printf("output_thread[STOP ISACTIVE]: PortAudio error3: %s\n",
									Pa_GetErrorText(err) );
					exit(-1);
				}
#endif
				audio->output_state = STOPPED;

				DEBUGF("output_thread STOP: %llu\n",audio->pa_streamtime_offset);
				pthread_cond_broadcast(&audio->output_cond);

				break;
				
			case PAUSE:
#ifndef PORTAUDIO_DEV
				if ( (err = Pa_StreamActive(audio->pa_stream) ) > 0)
				{
					err = Pa_StopStream(audio->pa_stream);
					if (err != paNoError)
					{
						printf("output_thread: PortAudio error10: %s\n", Pa_GetErrorText(err));	
						exit(-1);
					}
				}
				else
				{
					if ( err != paNoError)
					{
						printf("output_thread: PortAudio error11: %s\n",
						Pa_GetErrorText(err) );
						exit(-1);
					}
				}
#else
				if ( (err = Pa_IsStreamActive(audio->pa_stream)) > 0) {
					err = Pa_StopStream(audio->pa_stream);
					if (err != paNoError) {
						printf("output_thread[PAUSE]: PortAudio error3: %s\n",
								Pa_GetErrorText(err) );
						exit(-1);
					}
				} else if ( err != paNoError) {
					printf("output_thread[PAUSE ISACTIVE]: PortAudio error3: %s\n",
							Pa_GetErrorText(err) );
					exit(-1);
				}
#endif
				audio->output_state = PAUSED;

				DEBUGF("output_thread PAUSE: %llu\n",audio->pa_streamtime_offset);
				pthread_cond_broadcast(&audio->output_cond);

				break;

			case QUIT:
				DEBUGF("output_thread QUIT: %llu\n",audio->pa_streamtime_offset);
				break;

		}		
	}
	pthread_mutex_unlock(&audio->output_mutex);

#ifndef PORTAUDIO_DEV	
	if (audio->px_mixer != NULL) {
		Px_CloseMixer(audio->px_mixer);
		audio->px_mixer = NULL;
	}
#endif

	err = Pa_CloseStream(audio->pa_stream);

	if (err != paNoError) {
		printf("output_thread[exit]: PortAudio error3: %s\n", Pa_GetErrorText(err) );
		exit(-1);
	}

	audio->pa_stream = NULL;
	Pa_Terminate();

	DEBUGF("output_thread: PortAudio terminated\n");

	return 0;
}