Announcement

Collapse
No announcement yet.

Patch: (Linux) Remove dependence on PulseAudio

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Patch: (Linux) Remove dependence on PulseAudio

    Currently, OM has a dependence on PulseAudio, but only for changing the volume. The audio isn't output through PA, but rather however GStreamer is configured to output sound (which could be ALSA, PA, JACK, etc.). Furthermore, on a standard installation, the PulseAudio volume control is just forwarded to the ALSA mixer anyway.

    On some distros, such as Debian (which is what I run), PulseAudio isn't even installed by default. I don't like having to install PA just for one application, especially when that application doesn't even make use of PA, but only uses it for a volume control (which as I said, just gets forwarded to ALSA anyway).

    The following patch removes OM's dependence on PA, and instead uses the ALSA mixer for adjusting the volume.

    (Sorry for the indenting issues, I think the tab size was set wrong.)

    Thanks!
    -- Kevin

    Code:
    Index: MainWindow.cs
    ===================================================================
    --- MainWindow.cs	(revision 627)
    +++ MainWindow.cs	(working copy)
    @@ -24,52 +24,135 @@
         PowerDevil pd;
     	UDisks disks; 
     	MultimediaKeys.MultimediaKeysService mediaKeys=new MultimediaKeys.MultimediaKeysService();
    -	
    -	void checkVolume (int instance)
    +
    +    void checkAllVolume()
    +    {
    +        Int32 card = -1;
    +        while(ASound.snd_card_next(ref card) == 0)
    +        {
    +            raiseSystemEvent(eFunction.systemVolumeChanged, getVolume(card).ToString(), card.ToString(), "");
    +        }
    +    }
    +
    +	int getVolume (int instance)
     	{
    -        
    -		Process p=new Process();
    -        try
    +        String strDevice = "hw:" + instance; // Could be "default", or hw:X where X is the soundcard number
    +
    +        UIntPtr mixer;
    +        if(ASound.snd_mixer_open(out mixer, 0) != 0)
    +            return -1;
    +        if(ASound.snd_mixer_attach(mixer, strDevice) != 0)
             {
    -            ProcessStartInfo info = new ProcessStartInfo("pacmd", "list-sinks volume");
    -            info.WindowStyle = ProcessWindowStyle.Hidden;
    -            info.UseShellExecute = false;
    -            info.RedirectStandardOutput = true;
    -            p.StartInfo = info;
    -            p.Start();
    -            p.WaitForExit();
    +            ASound.snd_mixer_close(mixer);
    +            return -1;
             }
    -        catch (Exception) { return; }
    -		string response=p.StandardOutput.ReadToEnd();
    -		string[] lines=response.Split(new char[]{'\r','\n','\t'});
    -		int i=0;
    -		string vol="0";
    -		foreach(string line in lines)
    -		{
    -			if (line.StartsWith("volume:"))
    -			{
    -				vol=line.Substring(11,3).Trim();
    -			}
    -			else if(line.StartsWith("muted:"))
    -			{
    -				if (line.Contains("yes"))
    -				{
    -					if (instance!=i)
    -						raiseSystemEvent(eFunction.systemVolumeChanged,"-1",i.ToString(),"");
    -					else
    -						sendIt("3|"+i.ToString()+"|-1");
    -				}
    -				else
    -				{
    -					if (instance!=i)
    -						raiseSystemEvent(eFunction.systemVolumeChanged,vol,i.ToString(),"");
    -					else
    -						sendIt("3|"+i.ToString()+"|"+vol);
    -					i++;
    -				}
    -			}
    -		}
    +        if(ASound.snd_mixer_selem_register(mixer, (UIntPtr)0, (UIntPtr)0) != 0)
    +        {
    +            ASound.snd_mixer_detach(mixer, strDevice);
    +            ASound.snd_mixer_close(mixer);
    +            return -1;
    +        }
    +        if(ASound.snd_mixer_load(mixer) != 0)
    +        {
    +            ASound.snd_mixer_detach(mixer, strDevice);
    +            ASound.snd_mixer_close(mixer);
    +            return -1;
    +        }
    +
    +        UIntPtr mixerelem = ASound.snd_mixer_first_elem(mixer);
    +
    +        while (mixerelem != (UIntPtr)0)
    +        {
    +            String name = ASound.snd_mixer_selem_get_name(mixerelem);
    +            if (name.ToLower().Equals("master"))
    +                break;
    +            mixerelem = ASound.snd_mixer_elem_next(mixerelem);
    +        }
    +
    +        int volumePercent = 0;
    +        if(mixerelem != (UIntPtr)0)
    +        {
    +            Int32 playbackSwitch;
    +            ASound.snd_mixer_selem_get_playback_switch(mixerelem, 0, out playbackSwitch);
    +
    +            bool bMuted = (playbackSwitch == 0);
    +            if(bMuted)
    +            {
    +                volumePercent = -1;
    +            }
    +            else
    +            {
    +                IntPtr min, max;
    +                ASound.snd_mixer_selem_get_playback_volume_range(mixerelem, out min, out max);
    +
    +                IntPtr volume;
    +                ASound.snd_mixer_selem_get_playback_volume(mixerelem, 0, out volume);
    +
    +                volumePercent = (int)volume * 100 / (int)max;
    +            }
    +        }
    +        ASound.snd_mixer_detach(mixer, strDevice);
    +        ASound.snd_mixer_close(mixer);
    +
    +        return volumePercent;
     	}
    +
    +    void setVolume (int instance, int volumePercent)
    +    {
    +        String strDevice = "hw:" + instance; // Could be "default", or hw:X where X is the soundcard number
    +
    +        UIntPtr mixer;
    +        if(ASound.snd_mixer_open(out mixer, 0) != 0)
    +            return;
    +        if(ASound.snd_mixer_attach(mixer, strDevice) != 0)
    +        {
    +            ASound.snd_mixer_close(mixer);
    +            return;
    +        }
    +        if(ASound.snd_mixer_selem_register(mixer, (UIntPtr)0, (UIntPtr)0) != 0)
    +        {
    +            ASound.snd_mixer_detach(mixer, strDevice);
    +            ASound.snd_mixer_close(mixer);
    +            return;
    +        }
    +        if(ASound.snd_mixer_load(mixer) != 0)
    +        {
    +            ASound.snd_mixer_detach(mixer, strDevice);
    +            ASound.snd_mixer_close(mixer);
    +            return;
    +        }
    +
    +        UIntPtr mixerelem = ASound.snd_mixer_first_elem(mixer);
    +
    +        while (mixerelem != (UIntPtr)0)
    +        {
    +            String name = ASound.snd_mixer_selem_get_name(mixerelem);
    +            if (name.ToLower().Equals("master"))
    +                break;
    +            mixerelem = ASound.snd_mixer_elem_next(mixerelem);
    +        }
    +
    +        if(mixerelem != (UIntPtr)0)
    +        {
    +            if(volumePercent < 0)
    +            {
    +                ASound.snd_mixer_selem_set_playback_switch_all(mixerelem, 0);
    +            }
    +            else
    +            {
    +                ASound.snd_mixer_selem_set_playback_switch_all(mixerelem, 1);
    +
    +                IntPtr min, max;
    +                ASound.snd_mixer_selem_get_playback_volume_range(mixerelem, out min, out max);
    +
    +                IntPtr volume = (IntPtr)((int)max * volumePercent / 100);
    +                ASound.snd_mixer_selem_set_playback_volume_all(mixerelem, volume);
    +            }
    +        }
    +        ASound.snd_mixer_detach(mixer, strDevice);
    +        ASound.snd_mixer_close(mixer);
    +    }
    +
         static string DM;
     	public MainWindow () : base(Gtk.WindowType.Toplevel)
     	{
    @@ -111,7 +194,7 @@
     		disks.DeviceAdded+=added;
     		disks.DeviceChanged+=changed;
             disks.DeviceRemoved+=remove;
    -		checkVolume(-1);
    +        checkAllVolume();
             if (DM == "gnome")
             {
                 mediaKeys.keyPressed += new MultimediaKeys.MultimediaKeysService.MediaPlayerKeyPressedHandler(handleKey);
    @@ -264,16 +347,21 @@
                 switch (parts[0])
                 {
     				case "-1":
    -			            Thread.Sleep(250);
    -					    checkVolume(-1);
    +                        Thread.Sleep(250);
    +			            checkAllVolume();
     					break;
                     case "3": //GetData - System Volume
    -					int ret;
    -                    if (int.TryParse(arg1,out ret))
    -					{
    -                        if(ret>=0)
    -							checkVolume(ret);
    -					}
    +                    int instance;
    +                    if (int.TryParse(arg1,out instance) && instance >= 0)
    +                    {
    +                        try
    +                        {
    +                            sendIt("3|" + arg1 + "|" + getVolume(instance));
    +                        }
    +                        catch (Exception)
    +                        {
    +                        }
    +                    }
                         break;
     				case "32": //Plugins Loaded
     					foreach(ObjectPath path in disks.EnumerateDevices())
    @@ -303,24 +391,7 @@
                         if (int.TryParse(arg2,out val))
     						if (int.TryParse(arg1,out val))
     						{
    -							Process p=new Process();
    -							ProcessStartInfo info;
    -							if (val==-1)
    -								info=new ProcessStartInfo("pacmd","set-sink-mute "+arg2+" true");
    -							else
    -								info=new ProcessStartInfo("pacmd","set-sink-mute "+arg2+" false");
    -							info.WindowStyle=ProcessWindowStyle.Hidden;
    -							Process q=new Process();
    -							q.StartInfo=info;
    -							q.Start();
    -							if (val==-2)
    -								checkVolume(-1);
    -							if(val<0)
    -								return;
    -							info=new ProcessStartInfo("pacmd","set-sink-volume "+arg2+" "+((int)(val*655.35)).ToString());
    -							info.WindowStyle=ProcessWindowStyle.Hidden;
    -							p.StartInfo=info;
    -							p.Start();
    +                            setVolume(int.Parse(arg2), val);
     						}
                         break;
                     case "35": //Eject Disc
    Index: OMLinHal.csproj
    ===================================================================
    --- OMLinHal.csproj	(revision 627)
    +++ OMLinHal.csproj	(working copy)
    @@ -62,6 +62,7 @@
         <Compile Include="UDiscs.cs" />
         <Compile Include="Props.cs" />
         <Compile Include="MediaKeys.cs" />
    +    <Compile Include="ASound.cs" />
       </ItemGroup>
       <ItemGroup>
         <ProjectReference Include="..\..\FrontEnd\OpenMobile.Framework\OpenMobile.Framework.csproj">
    Index: ASound.cs
    ===================================================================
    --- ASound.cs	(revision 0)
    +++ ASound.cs	(revision 0)
    @@ -0,0 +1,56 @@
    +using System;
    +using System.Runtime.InteropServices;
    +
    +public class ASound
    +{
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_card_next(ref Int32 card);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_open(out UIntPtr mixer, UInt32 mode);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    +    public static extern Int32 snd_mixer_attach(UIntPtr mixer, String name);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    +    public static extern Int32 snd_mixer_detach(UIntPtr mixer, String name);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_close(UIntPtr mixer);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_selem_register(UIntPtr mixer, UIntPtr options, UIntPtr classp);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_load(UIntPtr mixer);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern UIntPtr snd_mixer_first_elem(UIntPtr mixer);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern UIntPtr snd_mixer_elem_next(UIntPtr elem);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_selem_get_playback_volume_range(UIntPtr elem, out IntPtr min, out IntPtr max);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_selem_set_playback_volume_all(UIntPtr mixer, IntPtr newValue);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_selem_get_playback_volume(UIntPtr mixer, Int32 channel, out IntPtr val);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_selem_set_playback_switch_all(UIntPtr mixer, Int32 newValue);
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl)]
    +    public static extern Int32 snd_mixer_selem_get_playback_switch(UIntPtr mixer, Int32 channel, out Int32 val);
    +
    +    public static String snd_mixer_selem_get_name(UIntPtr elem)
    +    {
    +        IntPtr ret = _snd_mixer_selem_get_name(elem);
    +        return Marshal.PtrToStringAnsi(ret);
    +    }
    +
    +    [DllImport("libasound.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint="snd_mixer_selem_get_name")]
    +    private static extern IntPtr _snd_mixer_selem_get_name(UIntPtr elem);
    +}

  • #2
    Removing pulse requirement sounds good to me. Even after all these years and all the hype, I have yet to have a time where PA has made things better vs worse.
    openMobile - Download
    Like what you see? Donations are always welcome

    Comment


    • #3
      I agree, I don't like PA. JACK (http://jackaudio.org/), on the other hand, would be incredibly useful to OM. I'm already routing all my audio through JACK, and using JackRack to host a few LADSPA plugins for doing audio processing (active crossover, and time alignment). Also, with Jack's ability to route audio from/to any input/output, including routing the output of one program to the input of another program, that would be a good way to implement the AudioRouter for Linux.

      Comment


      • #4
        Sounds awesome. I am a heavy linux user, but not up on programming under it to make use of linux specific items. An AudioRouter equivalent would be awesome.

        Sounds like you may be the guy I need to talk eventually about active under linux. VSTHost is about the only thing that would be stopping me from moving to linux some day. Can't say I have looked into the linux equivalents at all but have a 4 way active setup means I need it.
        openMobile - Download
        Like what you see? Donations are always welcome

        Comment


        • #5
          I was using RR on Windows until a couple months ago. I was using AudioMulch with a few VST plugins, along with Virtual Audio Cable. It's perfectly possible to get the same end result on Linux, using LADSPA plugins (the Linux equivalent of VST plugins). You don't have to use Jack or the JackRack to use LADSPA plugins on Linux, it just makes it easier. You can use LADSPA plugins with straight ALSA if you want (or PulseAudio). You can configure your .asoundrc file to load LADSPA plugins, but all the configuration is via text files, no nice graphical slider controls to adjust the parameters in realtime, which is what I like about JackRack.

          Comment


          • #6
            Awesome. I'll have to look into it all one of these days. I have used alsa to EQ the speakers on my desktop. Good to know there are options and they work for at least one other person out there. haha
            openMobile - Download
            Like what you see? Donations are always welcome

            Comment


            • #7
              BTW, I currently go back and forth between using RideRunner under Wine on Linux, and using OM. I also use Garmin Mobile PC under Wine. Both RR and GMPC work fine under Wine. I still use RR sometimes because OM isn't quite ready yet (but I'm doing my part to make it ready!)

              Comment


              • #8
                Personally not too worried about GPS here. I'll take my phone over the car apps the very rare times i need it. Nice having another linux nut around. I run it on my desktop, mythtv network, router, etc just haven't got around to helping OM make these last few jumps. So glad to see you are charging forward.
                openMobile - Download
                Like what you see? Donations are always welcome

                Comment


                • #9
                  Hey, another MythTV nut! Cool!

                  Comment


                  • #10
                    2 OTA, 1 HDPVR - Dish Network, 3 PXE Booting clients. How would we live without auto commercial skip?!?!?
                    openMobile - Download
                    Like what you see? Donations are always welcome

                    Comment


                    • #11
                      Commercial autoskip is definitely MythTV's killer feature (among its many killer features). 2 OTA, 2 ClearQAM, and 2 HD-PVR with FIOS. I used to PXE boot the bedroom frontend machine, but since I replaced that computer with an ION box, I just boot from the internal hard drive.

                      Comment


                      • #12
                        Thanks for the help on the linux side.
                        I'll try to include your changes in the code. Just have to make sure that they'll live happily alongside the windows system as well.

                        Thanks again.
                        Failure is not an option...
                        __________________________________________________ ______________________________
                        The only full multizone / multiscreen cross platform open source Front End -> OpenMobile

                        Comment


                        • #13
                          The changes are to OMLinHal, so it's Linux specific. It won't affect Windows at all.

                          Comment


                          • #14
                            Commited your changes.

                            I can't compile the linux part of OM so I can't verify the changes (I'll take your word for it and hope I got all your changes correct).
                            Failure is not an option...
                            __________________________________________________ ______________________________
                            The only full multizone / multiscreen cross platform open source Front End -> OpenMobile

                            Comment


                            • #15
                              Thanks Borte.

                              It won't build for me, because you wrapped the ASound class in the OMLinHal namespace, but almost nothing else in the project is in that namespace. My suggestion would be to add "namespace OMLinHal" to all of the files in the project. Also, I can tell you applied the changes by hand, instead of doing it the easy way running "patch -p0 < patch.txt".

                              If you want to test OM on Linux, including building, you can always run Ubuntu from a VirtualBox VM on your Windows machine.

                              Thanks!
                              -- Kevin

                              Edit: You can also apply patches using TortoiseSVN. Right click, and select Apply Patch.
                              Last edited by kross; 04-21-2011, 11:18 PM.

                              Comment

                              Working...
                              X