Update of /cvsroot/playerstage/code/player/server/drivers/audio
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8994
Modified Files:
alsa.cc alsa.h
Log Message:
Added mixer support to the ALSA driver
Index: alsa.h
===================================================================
RCS file: /cvsroot/playerstage/code/player/server/drivers/audio/alsa.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** alsa.h 10 Jun 2006 08:03:09 -0000 1.3
--- alsa.h 18 Jun 2006 06:59:08 -0000 1.4
***************
*** 56,59 ****
--- 56,85 ----
////////////////////////////////////////////////////////////////////////////////
+ // Describes an ALSA mixer element
+ typedef uint32_t ElemCap;
+ const ElemCap ELEMCAP_CAN_PLAYBACK = 0x0001;
+ const ElemCap ELEMCAP_CAN_CAPTURE = 0x0002;
+ const ElemCap ELEMCAP_COMMON = 0x0004; // Has a single
volume control for both playback and record
+ const ElemCap ELEMCAP_PLAYBACK_VOL = 0x0008;
+ const ElemCap ELEMCAP_CAPTURE_VOL = 0x0010;
+ const ElemCap ELEMCAP_COMMON_VOL = 0x0020;
+ const ElemCap ELEMCAP_PLAYBACK_SWITCH = 0x0040;
+ const ElemCap ELEMCAP_CAPTURE_SWITCH = 0x0080;
+ const ElemCap ELEMCAP_COMMON_SWITCH = 0x0100;
+ // const ElemCap ELEMCAP_PB_JOINED_SWITCH = 0x0200;
+ // const ElemCap ELEMCAP_CAP_JOINED_SWITCH = 0x0400;
+
+ typedef struct MixerElement
+ {
+ snd_mixer_elem_t *elem; // ALSA Mixer element structure
+ long minPlayVol, curPlayVol, maxPlayVol; // min, current and max
volume levels for playback
+ long minCapVol, curCapVol, maxCapVol; // min, current and max
volume levels for capture
+ long minComVol, curComVol, maxComVol; // min, current and max
volume levels for common
+ bool playMute, capMute, comMute; // Current mute
status
+ char *name; // Name of the
element
+ ElemCap caps; // Capabilities
+ } MixerElement;
+
+
////////////////////////////////////////////////////////////////////////////////
// The class for the driver
class Alsa : public Driver
***************
*** 71,74 ****
--- 97,101 ----
// Driver options
bool block; // If
should block while playing or return immediatly
+ char *device; // Name of the
mixer device to attach to
// uint32_t pbRate; // Sample rate
for playback
// int pbNumChannels; // Number of
sound channels for playback
***************
*** 79,82 ****
--- 106,112 ----
snd_pcm_stream_t pbStream; // Stream for playback
char *pcmName; // Name of the
sound interface
+ snd_mixer_t *mixerHandle; // Mixer for
controlling volume levels
+ MixerElement *mixerElements; // Elements of the mixer
+ uint32_t numElements; // Number of elements
// Other driver data
***************
*** 86,91 ****
--- 116,124 ----
int HandleWavePlayCmd (player_audio_wav_t *waveData);
int HandleSamplePlayCmd (player_audio_sample_item_t *data);
+ int HandleMixerChannelCmd (player_audio_mixer_channel_list_t
*data);
int HandleSampleLoadReq (player_audio_sample_t *data,
MessageQueue *resp_queue);
int HandleSampleRetrieveReq (player_audio_sample_t *data,
MessageQueue *resp_queue);
+ int HandleMixerChannelListReq
(player_audio_mixer_channel_list_detail_t *data, MessageQueue *resp_queue);
+ int HandleMixerChannelLevelReq
(player_audio_mixer_channel_list_t *data, MessageQueue *resp_queue);
// Internal functions
***************
*** 103,105 ****
--- 136,153 ----
bool SetHWParams (WaveData *wave);
bool PlayWave (WaveData *wave);
+
+ // Mixer functions
+ bool SetupMixer (void);
+ bool EnumMixerElements (void);
+ bool EnumElementCaps (MixerElement *element);
+ MixerElement* SplitElements (MixerElement *elements, uint32_t
&count);
+ void CleanUpMixerElements (MixerElement *elements, uint32_t
count);
+ void MixerDetailsToPlayer
(player_audio_mixer_channel_list_detail_t *dest);
+ void MixerLevelsToPlayer (player_audio_mixer_channel_list_t
*dest);
+ void SetElementLevel (uint32_t index, float level);
+ void SetElementMute (uint32_t index, player_bool_t mute);
+ void PublishMixerData (void);
+ float LevelToPlayer (long min, long max, long level);
+ long LevelFromPlayer (long min, long max, float level);
+ void PrintMixerElements (MixerElement *elements, uint32_t
count);
};
Index: alsa.cc
===================================================================
RCS file: /cvsroot/playerstage/code/player/server/drivers/audio/alsa.cc,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** alsa.cc 10 Jun 2006 08:03:09 -0000 1.3
--- alsa.cc 18 Jun 2006 06:59:08 -0000 1.4
***************
*** 38,48 ****
PLAYER_AUDIO_WAV_PLAY_CMD - Play raw PCM wave data
PLAYER_AUDIO_SAMPLE_PLAY_CMD - Play locally stored and remotely provided
samples
PLAYER_AUDIO_SAMPLE_LOAD_REQ - Store samples provided by remote clients (max
1MB)
PLAYER_AUDIO_SAMPLE_RETRIEVE_REQ - Send stored samples to remote clients (max
1MB)
Planned future support includes:
- Recording.
- - The callback method of managing buffers (to allow for blocking/nonblocking).
- - Mixer functionality.
@par Samples
--- 38,51 ----
PLAYER_AUDIO_WAV_PLAY_CMD - Play raw PCM wave data
PLAYER_AUDIO_SAMPLE_PLAY_CMD - Play locally stored and remotely provided
samples
+ PLAYER_AUDIO_MIXER_CHANNEL_CMD - Change volume levels
PLAYER_AUDIO_SAMPLE_LOAD_REQ - Store samples provided by remote clients (max
1MB)
+ PLAYER_AUDIO_MIXER_CHANNEL_LIST_REQ - Get channel details
+ PLAYER_AUDIO_MIXER_CHANNEL_LEVEL_REQ - Get volume levels
PLAYER_AUDIO_SAMPLE_RETRIEVE_REQ - Send stored samples to remote clients (max
1MB)
Planned future support includes:
+ - The callback method of managing buffers (to allow for blocking/nonblocking
and
+ less skippy playback).
- Recording.
@par Samples
***************
*** 80,83 ****
--- 83,89 ----
- Default: "plughw:0,0"
- The sound interface to use for playback/recording.
+ - mixerdevice (string)
+ - Default: "default"
+ - The device to attach the mixer interface to
- samples (tuple of strings)
- Default: empty
***************
*** 724,727 ****
--- 730,1267 ----
////////////////////////////////////////////////////////////////////////////////
+ // Mixer functions (finding channels, setting levels, etc)
+
////////////////////////////////////////////////////////////////////////////////
+
+ // Opens the mixer interface and enumerates the mixer capabilities
+ bool Alsa::SetupMixer (void)
+ {
+ // Open the mixer interface
+ if (snd_mixer_open (&mixerHandle, 0) < 0)
+ {
+ PLAYER_WARN ("Could not open mixer");
+ return false;
+ }
+
+ // Attach it to the device?
+ if (snd_mixer_attach (mixerHandle, device) < 0)
+ {
+ PLAYER_WARN ("Could not attach mixer");
+ return false;
+ }
+
+ // Register... something
+ if (snd_mixer_selem_register (mixerHandle, NULL, NULL) < 0)
+ {
+ PLAYER_WARN ("Could not register mixer");
+ return false;
+ }
+
+ // Load elements
+ if (snd_mixer_load (mixerHandle) < 0)
+ {
+ PLAYER_WARN ("Could not load mixer elements");
+ return false;
+ }
+
+ // Enumerate the elements
+ if (!EnumMixerElements ())
+ return false;
+
+ return true;
+ }
+
+ // Enumerates the mixer elements - i.e. finds out what each is
+ // Prepares the found data to be used with player
+ bool Alsa::EnumMixerElements (void)
+ {
+ MixerElement *elements = NULL;
+ snd_mixer_elem_t *elem = NULL;
+ uint32_t count = 0;
+
+ // Count the number of elements to store
+ for (elem = snd_mixer_first_elem (mixerHandle); elem != NULL; elem =
snd_mixer_elem_next (elem))
+ {
+ if (snd_mixer_elem_get_type (elem) == SND_MIXER_ELEM_SIMPLE &&
snd_mixer_selem_is_active (elem))
+ count++;
+ else
+ printf ("Skipping element\n");
+ }
+
+ // printf ("Found %d elements to enumerate\n", count);
+
+ // Allocate space to store the elements
+ if (count <= 0)
+ {
+ PLAYER_WARN ("Found zero or less mixer elements");
+ return false;
+ }
+ if ((elements = new MixerElement[count]) == NULL)
+ {
+ PLAYER_WARN1 ("Failed to allocate memory to store %d elements",
count);
+ return false;
+ }
+ memset (elements, 0, sizeof (MixerElement) * count);
+
+ // Get each element and its capabilities
+ uint32_t ii = 0;
+ for (elem = snd_mixer_first_elem (mixerHandle); elem != NULL; elem =
snd_mixer_elem_next (elem), ii++)
+ {
+ if (snd_mixer_elem_get_type (elem) == SND_MIXER_ELEM_SIMPLE &&
snd_mixer_selem_is_active (elem))
+ {
+ elements[ii].elem = elem;
+ if (!EnumElementCaps (&(elements[ii])))
+ {
+ CleanUpMixerElements (elements, count);
+ return false;
+ }
+ }
+ }
+ uint32_t newCount = count;
+ // Split channels capable of both playback and capture (makes it easier
to manage via player)
+ if ((mixerElements = SplitElements (elements, newCount)) == NULL)
+ {
+ PLAYER_WARN ("Error splitting mixer elements");
+ CleanUpMixerElements (elements, count);
+ return false;
+ }
+ numElements = newCount;
+
+ CleanUpMixerElements (elements, count);
+
+ // PrintMixerElements (mixerElements, numElements);
+ return true;
+ }
+
+ // Enumerates the capabilities of a single element
+ bool Alsa::EnumElementCaps (MixerElement *element)
+ {
+ int temp = 0;
+ snd_mixer_elem_t *elem = element->elem;
+ if (!elem)
+ {
+ PLAYER_WARN ("Attempted to enumerate NULL element pointer");
+ return false;
+ }
+
+ // Get the element name
+ element->name = strdup (snd_mixer_selem_get_name (elem));
+ // Get capabilities
+ // Volumes
+ if (snd_mixer_selem_has_playback_volume (elem))
+ element->caps |= ELEMCAP_PLAYBACK_VOL;
+ if (snd_mixer_selem_has_capture_volume (elem))
+ element->caps |= ELEMCAP_CAPTURE_VOL;
+ if (snd_mixer_selem_has_common_volume (elem))
+ element->caps |= ELEMCAP_COMMON_VOL;
+ // Switches
+ if (snd_mixer_selem_has_playback_switch (elem))
+ element->caps |= ELEMCAP_PLAYBACK_SWITCH;
+ if (snd_mixer_selem_has_capture_switch (elem))
+ element->caps |= ELEMCAP_CAPTURE_SWITCH;
+ if (snd_mixer_selem_has_common_switch (elem))
+ element->caps |= ELEMCAP_COMMON_SWITCH;
+ // Joined switches
+ // if (snd_mixer_selem_has_playback_switch (elem))
+ // mixerElements[index].caps |= ELEMCAP_PB_JOINED_SWITCH;
+ // if (snd_mixer_selem_has_capture_switch (elem))
+ // mixerElements[index].caps |= ELEMCAP_CAP_JOINED_SWITCH;
+
+ element->playMute = true;
+ element->capMute = true;
+ element->comMute = true;
+
+ // printf ("Found mixer element: %s\n", element->name);
+ // Find channels for this element
+ for (int ii = -1; ii <= (int) SND_MIXER_SCHN_LAST; ii++)
+ {
+ if (snd_mixer_selem_has_playback_channel (elem,
static_cast<snd_mixer_selem_channel_id_t> (ii)))
+ {
+ // printf ("Element has playback channel %d: %s\n", ii,
snd_mixer_selem_channel_name (static_cast<snd_mixer_selem_channel_id_t> (ii)));
+ element->caps |= ELEMCAP_CAN_PLAYBACK;
+ // Get the current volume of this channel and make it
the element one, if don't have that yet
+ if (!element->curPlayVol)
+ snd_mixer_selem_get_playback_volume (elem,
static_cast<snd_mixer_selem_channel_id_t> (ii), &(element->curPlayVol));
+ // Get the mute status of this channel - if unmuted,
then set element to unmuted
+ if (element->caps & ELEMCAP_PLAYBACK_SWITCH)
+ {
+ snd_mixer_selem_get_playback_switch (elem,
static_cast<snd_mixer_selem_channel_id_t> (ii), &temp);
+ if (temp)
+ element->playMute = false;
+ }
+ }
+ if (snd_mixer_selem_has_capture_channel (elem,
static_cast<snd_mixer_selem_channel_id_t> (ii)))
+ {
+ // printf ("Element has capture channel %d: %s\n", ii,
snd_mixer_selem_channel_name (static_cast<snd_mixer_selem_channel_id_t> (ii)));
+ element->caps |= ELEMCAP_CAN_CAPTURE;
+ // Get the current volume of this channel and make it
the element one, if don't have that yet
+ if (!element->curCapVol)
+ snd_mixer_selem_get_capture_volume (elem,
static_cast<snd_mixer_selem_channel_id_t> (ii), &(element->curCapVol));
+ // Get the mute status of this channel - if unmuted,
then set element to unmuted
+ if (element->caps & ELEMCAP_CAPTURE_SWITCH)
+ {
+ snd_mixer_selem_get_playback_switch (elem,
static_cast<snd_mixer_selem_channel_id_t> (ii), &temp);
+ if (temp)
+ element->capMute = false;
+ }
+ }
+ }
+
+ // Get volume ranges
+ if ((element->caps & ELEMCAP_CAN_PLAYBACK) && (element->caps &
ELEMCAP_PLAYBACK_VOL))
+ {
+ snd_mixer_selem_get_playback_volume_range (elem,
&(element->minPlayVol), &(element->maxPlayVol));
+ }
+ if ((element->caps & ELEMCAP_CAN_CAPTURE) && (element->caps &
ELEMCAP_CAPTURE_VOL))
+ {
+ snd_mixer_selem_get_capture_volume_range (elem,
&(element->minCapVol), &(element->maxCapVol));
+ }
+ if (element->caps & ELEMCAP_COMMON_VOL)
+ {
+ // If statement on next line isn't a typo, min vol will
probably be zero whether it's been filled in or not, max won't
+ element->minComVol = element->maxPlayVol ? element->minPlayVol
: element->minCapVol;
+ element->maxComVol = element->maxPlayVol ? element->maxPlayVol
: element->maxCapVol;
+ }
+
+ // Common mute status
+ if (element->caps & ELEMCAP_COMMON_SWITCH)
+ element->comMute = element->playMute ? element->playMute :
element->capMute;
+
+ // printf ("Element volume levels:\n");
+ // printf ("Playback:\t%ld, %ld, %ld, %s\n", element->minPlayVol,
element->curPlayVol, element->maxPlayVol, element->playMute ? "Muted" :
"Unmuted");
+ // printf ("Capture:\t%ld, %ld, %ld, %s\n", element->minCapVol,
element->curCapVol, element->maxCapVol, element->capMute ? "Muted" : "Unmuted");
+ // printf ("Common:\t%ld, %ld, %ld, %s\n", element->minComVol,
element->curComVol, element->maxComVol, element->comMute ? "Muted" : "Unmuted");
+
+ return true;
+ }
+
+ // Splits elements into two separate elements for those elements that are
capable
+ // of entirely separate playback and capture
+ MixerElement* Alsa::SplitElements (MixerElement *elements, uint32_t &count)
+ {
+ MixerElement *result = NULL;
+ // Count the number of elements we will get as a result:
+ // Each current element adds 2 if it does both with separate controls,
1 otherwise
+ uint32_t numSplitElements = 0;
+ for (uint32_t ii = 0; ii < count; ii++)
+ {
+ if ((elements[ii].caps & ELEMCAP_CAN_PLAYBACK) &&
(elements[ii].caps & ELEMCAP_CAN_CAPTURE) &&
+ !(elements[ii].caps & ELEMCAP_COMMON_VOL) &&
!(elements[ii].caps & ELEMCAP_COMMON_SWITCH))
+ numSplitElements += 2;
+ else
+ numSplitElements += 1;
+ }
+
+ // Allocate space for the new array of elements
+ if (numSplitElements <= 0)
+ {
+ PLAYER_WARN ("Found zero or less split mixer elements");
+ return NULL;
+ }
+ if ((result = new MixerElement[numSplitElements]) == NULL)
+ {
+ PLAYER_WARN1 ("Failed to allocate memory to store %d split
elements", numSplitElements);
+ return NULL;
+ }
+ memset (result, 0, sizeof (MixerElement) * numSplitElements);
+
+ // Copy relevant data across
+ uint32_t currentIndex = 0;
+ for (uint32_t ii = 0; ii < count; ii++)
+ {
+ // Element capable of separate playback and record
+ if ((elements[ii].caps & ELEMCAP_CAN_PLAYBACK) &&
(elements[ii].caps & ELEMCAP_CAN_CAPTURE) &&
+ !(elements[ii].caps & ELEMCAP_COMMON_VOL) &&
!(elements[ii].caps & ELEMCAP_COMMON_SWITCH))
+ {
+ // In this case, split the element, so will set data
for currentIndex and currentIndex+1
+ // Playback element
+ result[currentIndex].elem = elements[ii].elem;
+ result[currentIndex].caps = ELEMCAP_CAN_PLAYBACK;
+ result[currentIndex].minPlayVol =
elements[ii].minPlayVol;
+ result[currentIndex].curPlayVol =
elements[ii].curPlayVol;
+ result[currentIndex].maxPlayVol =
elements[ii].maxPlayVol;
+ result[currentIndex].playMute = elements[ii].playMute;
+ result[currentIndex].name = reinterpret_cast<char*>
(malloc (strlen (elements[ii].name) + strlen (" (Playback)") + 1));
+ strncpy (result[currentIndex].name, elements[ii].name,
strlen (elements[ii].name) + 1);
+ strncpy (&(result[currentIndex].name[strlen
(elements[ii].name)]), " (Playback)", strlen (" (Playback)") + 1);
+
+ // Capture element
+ result[currentIndex + 1].elem = elements[ii].elem;
+ result[currentIndex + 1].caps = ELEMCAP_CAN_CAPTURE;
+ result[currentIndex + 1].minCapVol =
elements[ii].minCapVol;
+ result[currentIndex + 1].curCapVol =
elements[ii].curCapVol;
+ result[currentIndex + 1].maxCapVol =
elements[ii].maxCapVol;
+ result[currentIndex + 1].capMute = elements[ii].capMute;
+ result[currentIndex + 1].name = reinterpret_cast<char*>
(malloc (strlen (elements[ii].name) + strlen (" (Capture)") + 1));
+ strncpy (result[currentIndex + 1].name,
elements[ii].name, strlen (elements[ii].name) + 1);
+ strncpy (&(result[currentIndex + 1].name[strlen
(elements[ii].name)]), " (Capture)", strlen (" (Capture)") + 1);
+
+ currentIndex += 2;
+ }
+ // Element that can only playback
+ else if ((elements[ii].caps & ELEMCAP_CAN_PLAYBACK) &&
!(elements[ii].caps & ELEMCAP_CAN_CAPTURE))
+ {
+ // Just copy in this case
+ result[currentIndex].elem = elements[ii].elem;
+ result[currentIndex].caps = ELEMCAP_CAN_PLAYBACK;
+ result[currentIndex].minPlayVol =
elements[ii].minPlayVol;
+ result[currentIndex].curPlayVol =
elements[ii].curPlayVol;
+ result[currentIndex].maxPlayVol =
elements[ii].maxPlayVol;
+ result[currentIndex].playMute = elements[ii].playMute;
+ result[currentIndex].name = strdup (elements[ii].name);
+
+ currentIndex += 1;
+ }
+ // Element that can only capture
+ else if (!(elements[ii].caps & ELEMCAP_CAN_PLAYBACK) &&
(elements[ii].caps & ELEMCAP_CAN_CAPTURE))
+ {
+ // Just copy in this case
+ result[currentIndex].elem = elements[ii].elem;
+ result[currentIndex].caps = ELEMCAP_CAN_CAPTURE;
+ result[currentIndex].minCapVol = elements[ii].minCapVol;
+ result[currentIndex].curCapVol = elements[ii].curCapVol;
+ result[currentIndex].maxCapVol = elements[ii].maxCapVol;
+ result[currentIndex].capMute = elements[ii].capMute;
+ result[currentIndex].name = strdup (elements[ii].name);
+
+ currentIndex += 1;
+ }
+ // Element that can do both but cannot set independent volumes
+ else
+ {
+ result[currentIndex].elem = elements[ii].elem;
+ result[currentIndex].caps = ELEMCAP_CAN_PLAYBACK &
ELEMCAP_CAN_CAPTURE & ELEMCAP_COMMON;
+ result[currentIndex].minComVol = elements[ii].minComVol;
+ result[currentIndex].curComVol = elements[ii].curComVol;
+ result[currentIndex].maxComVol = elements[ii].maxComVol;
+ result[currentIndex].comMute = elements[ii].comMute;
+ result[currentIndex].name = strdup (elements[ii].name);
+
+ currentIndex += 1;
+ }
+ }
+
+ count = numSplitElements;
+ return result;
+ }
+
+ // Cleans up mixer element data
+ void Alsa::CleanUpMixerElements (MixerElement *elements, uint32_t count)
+ {
+ for (uint32_t ii = 0; ii < count; ii++)
+ {
+ if (elements[ii].name)
+ free (elements[ii].name);
+ }
+ delete[] elements;
+ }
+
+ // Converts mixer information to player details
+ void Alsa::MixerDetailsToPlayer (player_audio_mixer_channel_list_detail_t
*dest)
+ {
+ memset (dest, 0, sizeof (player_audio_mixer_channel_list_detail_t));
+
+ dest->details_count = numElements;
+ dest->default_output = 0;
+ dest->default_input = 0; // TODO: figure out what the default
is... driver option maybe?
+
+ for (uint32_t ii = 0; ii < numElements; ii++)
+ {
+ dest->details[ii].name_count = strlen (mixerElements[ii].name);
+ strncpy (dest->details[ii].name, mixerElements[ii].name, strlen
(mixerElements[ii].name) + 1);
+ if ((mixerElements[ii].caps & ELEMCAP_CAN_PLAYBACK) &&
!(mixerElements[ii].caps & ELEMCAP_CAN_CAPTURE))
+ dest->details[ii].caps =
PLAYER_AUDIO_MIXER_CHANNEL_TYPE_OUTPUT;
+ else if (!(mixerElements[ii].caps & ELEMCAP_CAN_PLAYBACK) &&
(mixerElements[ii].caps & ELEMCAP_CAN_CAPTURE))
+ dest->details[ii].caps =
PLAYER_AUDIO_MIXER_CHANNEL_TYPE_INPUT;
+ else
+ dest->details[ii].caps =
PLAYER_AUDIO_MIXER_CHANNEL_TYPE_INPUT & PLAYER_AUDIO_MIXER_CHANNEL_TYPE_OUTPUT;
+ }
+ }
+
+ // Converts mixer information to player levels
+ void Alsa::MixerLevelsToPlayer (player_audio_mixer_channel_list_t *dest)
+ {
+ memset (dest, 0, sizeof (player_audio_mixer_channel_list_t));
+
+ dest->channels_count = numElements;
+
+ for (uint32_t ii = 0; ii < numElements; ii++)
+ {
+ long min = 0, cur = 0, max = 0;
+ bool mute = false;
+ if (mixerElements[ii].caps & ELEMCAP_CAN_PLAYBACK)
+ {
+ min = mixerElements[ii].minPlayVol;
+ cur = mixerElements[ii].curPlayVol;
+ max = mixerElements[ii].maxPlayVol;
+ mute = mixerElements[ii].playMute;
+ }
+ else if (mixerElements[ii].caps & ELEMCAP_CAN_CAPTURE)
+ {
+ min = mixerElements[ii].minCapVol;
+ cur = mixerElements[ii].curCapVol;
+ max = mixerElements[ii].maxCapVol;
+ mute = mixerElements[ii].capMute;
+ }
+ else if (mixerElements[ii].caps & ELEMCAP_COMMON)
+ {
+ min = mixerElements[ii].minComVol;
+ cur = mixerElements[ii].curComVol;
+ max = mixerElements[ii].maxComVol;
+ mute = mixerElements[ii].comMute;
+ }
+ dest->channels[ii].amplitude = LevelToPlayer (min, max, cur);
+ dest->channels[ii].active.state = mute ? 0 : 1;
+ dest->channels[ii].index = ii;
+ }
+ }
+
+ // Sets the volume level of an element
+ void Alsa::SetElementLevel (uint32_t index, float level)
+ {
+ long newValue = 0;
+
+ if (mixerElements[index].caps & ELEMCAP_CAN_PLAYBACK)
+ {
+ // Calculate the new level
+ newValue = LevelFromPlayer (mixerElements[index].minPlayVol,
mixerElements[index].maxPlayVol, level);
+ // Set the volume for all channels in this element
+ if (snd_mixer_selem_set_playback_volume_all
(mixerElements[index].elem, newValue) < 0)
+ {
+ PLAYER_WARN1 ("Error setting playback level for element
%d", index);
+ }
+ else
+ mixerElements[index].curPlayVol = newValue;
+ }
+ else if (mixerElements[index].caps & ELEMCAP_CAN_CAPTURE)
+ {
+ // Calculate the new level
+ newValue = LevelFromPlayer (mixerElements[index].minCapVol,
mixerElements[index].maxCapVol, level);
+ // Set the volume for all channels in this element
+ if (snd_mixer_selem_set_capture_volume_all
(mixerElements[index].elem, newValue) < 0)
+ {
+ PLAYER_WARN1 ("Error setting capture level for element
%d", index);
+ }
+ else
+ mixerElements[index].curCapVol = newValue;
+ }
+ else if (mixerElements[index].caps & ELEMCAP_COMMON)
+ {
+ // Calculate the new level
+ newValue = LevelFromPlayer (mixerElements[index].minComVol,
mixerElements[index].maxComVol, level);
+ // Set the volume for all channels in this element
+ if (snd_mixer_selem_set_playback_volume_all
(mixerElements[index].elem, newValue) < 0)
+ {
+ PLAYER_WARN1 ("Error setting common level for element
%d", index);
+ }
+ else
+ mixerElements[index].curComVol = newValue;
+ }
+ }
+
+ // Sets mute for an element
+ void Alsa::SetElementMute (uint32_t index, player_bool_t mute)
+ {
+ if (mixerElements[index].caps & ELEMCAP_CAN_PLAYBACK)
+ {
+ // Set the mute for all channels in this element
+ if (snd_mixer_selem_set_playback_switch_all
(mixerElements[index].elem, mute.state ? 1 : 0) < 0)
+ {
+ PLAYER_WARN1 ("Error setting playback mute for element
%d", index);
+ }
+ else
+ mixerElements[index].playMute = mute.state ? true :
false;
+ }
+ else if (mixerElements[index].caps & ELEMCAP_CAN_CAPTURE)
+ {
+ // Set the mute for all channels in this element
+ if (snd_mixer_selem_set_capture_volume_all
(mixerElements[index].elem, mute.state ? 1 : 0) < 0)
+ {
+ PLAYER_WARN1 ("Error setting capture mute for element
%d", index);
+ }
+ else
+ mixerElements[index].capMute = mute.state ? true :
false;
+ }
+ else if (mixerElements[index].caps & ELEMCAP_COMMON)
+ {
+ // Set the mute for all channels in this element
+ if (snd_mixer_selem_set_playback_volume_all
(mixerElements[index].elem, mute.state ? 1 : 0) < 0)
+ {
+ PLAYER_WARN1 ("Error setting common mute for element
%d", index);
+ }
+ else
+ mixerElements[index].comMute = mute.state ? true :
false;
+ }
+ }
+
+ // Publishes mixer information as a data message
+ void Alsa::PublishMixerData (void)
+ {
+ player_audio_mixer_channel_list_t data;
+
+ MixerLevelsToPlayer (&data);
+ Publish (device_addr, NULL, PLAYER_MSGTYPE_DATA,
PLAYER_AUDIO_MIXER_CHANNEL_DATA, reinterpret_cast<void*> (&data), sizeof
(player_audio_mixer_channel_list_t), NULL);
+ }
+
+ // Converts an element level from a long to a float between 0 and 1
+ float Alsa::LevelToPlayer (long min, long max, long level)
+ {
+ float result = 0.0f;
+ if ((max - min) != 0)
+ result = static_cast<float> (level - min) / static_cast<float>
(max - min);
+ return result;
+ }
+
+ // Converts an element level from a float between 0 and 1 to a long between
min and max
+ long Alsa::LevelFromPlayer (long min, long max, float level)
+ {
+ long result = static_cast<long> ((max - min) * level);
+ return result;
+ }
+
+ // Handy debug function
+ void Alsa::PrintMixerElements (MixerElement *elements, uint32_t count)
+ {
+ long min, cur, max;
+ bool mute;
+ printf ("Mixer elements:\n");
+ for (uint32_t ii = 0; ii < count; ii++)
+ {
+ if (elements[ii].caps & ELEMCAP_CAN_PLAYBACK)
+ {
+ min = elements[ii].minPlayVol;
+ cur = elements[ii].curPlayVol;
+ max = elements[ii].maxPlayVol;
+ mute = elements[ii].playMute;
+ }
+ else if (elements[ii].caps & ELEMCAP_CAN_CAPTURE)
+ {
+ min = elements[ii].minCapVol;
+ cur = elements[ii].curCapVol;
+ max = elements[ii].maxCapVol;
+ mute = elements[ii].capMute;
+ }
+ else if (elements[ii].caps & ELEMCAP_COMMON)
+ {
+ min = elements[ii].minComVol;
+ cur = elements[ii].curComVol;
+ max = elements[ii].maxComVol;
+ mute = elements[ii].comMute;
+ }
+ printf ("Element %d:\t%s\n", ii, elements[ii].name);
+ printf ("Capabilities:\t");
+ if (elements[ii].caps & ELEMCAP_CAN_PLAYBACK)
+ printf ("playback\t");
+ if (elements[ii].caps & ELEMCAP_CAN_CAPTURE)
+ printf ("capture\t");
+ if (elements[ii].caps & ELEMCAP_COMMON)
+ printf ("common");
+ printf ("\n");
+ printf ("Volume range:\t%ld->%ld\n", min, max);
+ printf ("Current volume:\t%ld\n", cur);
+ printf ("Active:\t%s\n", mute ? "No" : "Yes");
+ }
+ }
+
+
////////////////////////////////////////////////////////////////////////////////
// Driver management
////////////////////////////////////////////////////////////////////////////////
***************
*** 732,742 ****
: Driver (cf, section, false, PLAYER_MSGQUEUE_DEFAULT_MAXLEN,
PLAYER_AUDIO_CODE)
{
! pcmName = NULL;
samplesHead = samplesTail = NULL;
nextSampleIdx = 0;
// Read the config file options - see header for descriptions if not
here
block = cf->ReadBool (section, "block", true);
pcmName = strdup (cf->ReadString (section, "interface", "plughw:0,0"));
// pbRate = cf->ReadInt (section, "pb_rate", 44100);
// pbNumChannels = cf->ReadInt (section, "pb_numchannels", 2);
--- 1272,1284 ----
: Driver (cf, section, false, PLAYER_MSGQUEUE_DEFAULT_MAXLEN,
PLAYER_AUDIO_CODE)
{
! pcmName = device = NULL;
samplesHead = samplesTail = NULL;
nextSampleIdx = 0;
+ mixerElements = NULL;
// Read the config file options - see header for descriptions if not
here
block = cf->ReadBool (section, "block", true);
pcmName = strdup (cf->ReadString (section, "interface", "plughw:0,0"));
+ device = strdup (cf->ReadString (section, "mixerdevice", "default"));
// pbRate = cf->ReadInt (section, "pb_rate", 44100);
// pbNumChannels = cf->ReadInt (section, "pb_numchannels", 2);
***************
*** 763,766 ****
--- 1305,1310 ----
if (pcmName)
free (pcmName);
+ if (device)
+ free (device);
if (samplesHead)
{
***************
*** 796,799 ****
--- 1340,1349 ----
}
+ if (!SetupMixer ())
+ {
+ PLAYER_WARN ("Error opening mixer, mixer interface will not be
available");
+ mixerHandle = NULL;
+ }
+
StartThread ();
// printf ("Alsa driver initialised\n");
***************
*** 808,811 ****
--- 1358,1382 ----
StopThread ();
+
+ if (mixerHandle)
+ {
+ if (numElements > 0)
+ {
+ CleanUpMixerElements (mixerElements, numElements);
+ }
+ if (snd_mixer_detach (mixerHandle, device) < 0)
+ PLAYER_WARN ("Error detaching mixer interface");
+ else
+ {
+ if (snd_mixer_close (mixerHandle) < 0)
+ PLAYER_WARN ("Error closing mixer interface");
+ else
+ {
+ // TODO: Figure out why this causes a segfault
+ // snd_mixer_free (mixerHandle);
+ }
+ }
+ }
+
snd_pcm_close (pcmHandle);
***************
*** 820,823 ****
--- 1391,1397 ----
void Alsa::Main (void)
{
+ // If mixer is enabled, send out some mixer data
+ /* if (mixerHandle)
+ PublishMixerData ();*/
while (1)
{
***************
*** 903,906 ****
--- 1477,1493 ----
}
+ int Alsa::HandleMixerChannelCmd (player_audio_mixer_channel_list_t *data)
+ {
+ for (uint32_t ii = 0; ii < data->channels_count; ii++)
+ {
+ SetElementLevel (data->channels[ii].index,
data->channels[ii].amplitude);
+ SetElementMute (data->channels[ii].index,
data->channels[ii].active);
+ }
+
+ PublishMixerData ();
+
+ return 0;
+ }
+
int Alsa::HandleSampleLoadReq (player_audio_sample_t *data, MessageQueue
*resp_queue)
{
***************
*** 1029,1032 ****
--- 1616,1637 ----
}
+ int Alsa::HandleMixerChannelListReq (player_audio_mixer_channel_list_detail_t
*data, MessageQueue *resp_queue)
+ {
+ player_audio_mixer_channel_list_detail_t result;
+ MixerDetailsToPlayer (&result);
+ Publish (device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK,
PLAYER_AUDIO_MIXER_CHANNEL_LIST_REQ, &result, sizeof
(player_audio_mixer_channel_list_detail_t), NULL);
+
+ return 0;
+ }
+
+ int Alsa::HandleMixerChannelLevelReq (player_audio_mixer_channel_list_t
*data, MessageQueue *resp_queue)
+ {
+ player_audio_mixer_channel_list_t result;
+ MixerLevelsToPlayer (&result);
+ Publish (device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK,
PLAYER_AUDIO_MIXER_CHANNEL_LEVEL_REQ, &result, sizeof
(player_audio_mixer_channel_list_t), NULL);
+
+ return 0;
+ }
+
// Message processing
int Alsa::ProcessMessage (MessageQueue *resp_queue, player_msghdr *hdr, void
*data)
***************
*** 1036,1041 ****
--- 1641,1649 ----
HANDLE_CAPABILITY_REQUEST (device_addr, resp_queue, hdr, data,
PLAYER_MSGTYPE_CMD, PLAYER_AUDIO_WAV_PLAY_CMD);
HANDLE_CAPABILITY_REQUEST (device_addr, resp_queue, hdr, data,
PLAYER_MSGTYPE_CMD, PLAYER_AUDIO_SAMPLE_PLAY_CMD);
+ HANDLE_CAPABILITY_REQUEST (device_addr, resp_queue, hdr, data,
PLAYER_MSGTYPE_CMD, PLAYER_AUDIO_MIXER_CHANNEL_CMD);
HANDLE_CAPABILITY_REQUEST (device_addr, resp_queue, hdr, data,
PLAYER_MSGTYPE_REQ, PLAYER_AUDIO_SAMPLE_LOAD_REQ);
HANDLE_CAPABILITY_REQUEST (device_addr, resp_queue, hdr, data,
PLAYER_MSGTYPE_REQ, PLAYER_AUDIO_SAMPLE_RETRIEVE_REQ);
+ HANDLE_CAPABILITY_REQUEST (device_addr, resp_queue, hdr, data,
PLAYER_MSGTYPE_REQ, PLAYER_AUDIO_MIXER_CHANNEL_LIST_REQ);
+ HANDLE_CAPABILITY_REQUEST (device_addr, resp_queue, hdr, data,
PLAYER_MSGTYPE_REQ, PLAYER_AUDIO_MIXER_CHANNEL_LEVEL_REQ);
// Commands
***************
*** 1050,1053 ****
--- 1658,1666 ----
return 0;
}
+ else if (Message::MatchMessage (hdr, PLAYER_MSGTYPE_CMD,
PLAYER_AUDIO_MIXER_CHANNEL_CMD, this->device_addr))
+ {
+ HandleMixerChannelCmd
(reinterpret_cast<player_audio_mixer_channel_list_t*> (data));
+ return 0;
+ }
// Requests
else if (Message::MatchMessage (hdr, PLAYER_MSGTYPE_REQ,
PLAYER_AUDIO_SAMPLE_LOAD_REQ, this->device_addr))
***************
*** 1059,1062 ****
--- 1672,1684 ----
return HandleSampleRetrieveReq
(reinterpret_cast<player_audio_sample_t*> (data), resp_queue);
}
+ else if (Message::MatchMessage (hdr, PLAYER_MSGTYPE_REQ,
PLAYER_AUDIO_MIXER_CHANNEL_LIST_REQ, this->device_addr))
+ {
+ return HandleMixerChannelListReq
(reinterpret_cast<player_audio_mixer_channel_list_detail_t*> (data),
resp_queue);
+ }
+ else if (Message::MatchMessage (hdr, PLAYER_MSGTYPE_REQ,
PLAYER_AUDIO_MIXER_CHANNEL_LEVEL_REQ, this->device_addr))
+ {
+ return HandleMixerChannelLevelReq
(reinterpret_cast<player_audio_mixer_channel_list_t*> (data), resp_queue);
+ }
+
return -1;
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit