arsteeg
Posts: 3
Joined: Sun Apr 05, 2015 10:29 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Tue Apr 07, 2015 2:59 am

Thank You,

I didn't even think of the HiFiBerry, but it was causing my problems.

For the DAC+:
"GPIOs 18-21 are used for the sound interface. You can’t use them for any other purpose."
https://www.hifiberry.com/guides/gpio-u ... -products/

erotavlas
Posts: 41
Joined: Wed Mar 11, 2015 5:26 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Wed Apr 22, 2015 1:32 pm

Hi,
I'm looking for a way to use Raspbian as A2DP server and I found many solutions, but the more proper is to use Pulseaudio. Then, I found this thread. I did not have the time to try the proposed solution and I would like to know if it works. Moreover, are there any news?

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Wed Apr 22, 2015 2:02 pm

erotavlas wrote:I'm looking for a way to use Raspbian as A2DP server....and I would like to know if it works.
It works. But realize this is a guide for setting up the Pi as an A2DP client, not server, using non-standard source code under Wheezy.
Douglas6 wrote:Note that BT streaming can be done without building packages from source (use one of the the several (not so good) guides already extant on the web). Attempt this guide only if you're comfortable with the Linux command line, and you REALLY want the latest Bluetooth stack.
I would recommend following a guide using the standard BlueZ (4.99) and PulseAudio (4.x) packages, except I haven't found one I could recommend.

AlessandroFerri
Posts: 61
Joined: Tue Apr 02, 2013 5:44 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Fri Apr 24, 2015 5:48 pm

Hello everyone.
Is there anyone who can tell me if the parameters to compile pulseaudio 5.0 for raspberry Pi2 are the same as for the raspberry B+ or change?
I used those given at the beginning of this post:

Code: Select all

./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-bluez4 --disable-rpath --with-module-dir=/usr/lib/pulse/modules
make
sudo make install
I realized that PulseAudio works with only one core on raspberry Pi2 and not with four cores. I see by running top. I think that must be given other parameters to be included on ./configure and make.

Does anyone have any suggestion or solution?

erotavlas
Posts: 41
Joined: Wed Mar 11, 2015 5:26 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Sun May 03, 2015 2:13 pm

Hi,
I read this thread and I found it very useful. In particular, I was able to install Pulseaudio 6 together with Bluez 5.30 on Jessie (after apt-get dist-upgrade from Wheezy) running in Raspberry pi 1 and 2. Now, I'm looking for a way to automate the process of bluetooth pairing that is the only one that requires to log inside Raspbian and to use CLI.
So I know that the necessary step are:

Code: Select all

bluetoothctl
power on
discoverable on
agent on
default-agent
pairable on
For instance, I can write a script like this

Code: Select all

#!/bin/bash
bluetoothctl << EOF
power on
discoverable on
agent on
default-agent
pairable on
EOF
but I do not know how to intercept the pairing request coming from devices and how to answer yes. After, I would like to run it as daemon.
Thank you

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Sun May 03, 2015 2:52 pm

You could probably write a a bash script using expect. I've got a python script that automatically accepts pairing requests, I'll try and dig it out today.

erotavlas
Posts: 41
Joined: Wed Mar 11, 2015 5:26 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Sun May 03, 2015 2:56 pm

Ok, thank you very much. This answer is also important for this thread viewtopic.php?t=53299&p=409980

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Mon May 04, 2015 4:48 pm

Copy the following code to blueagent5.py and make it executable. Run it with 'sudo blueagent5.py --pin 1234' to change from the default PIN of '0000'. You will need to have installed python-gobject. It will need root permissions to run. It logs to /var/log/syslog. Daemonizing it on Jessie is up to you, I'm not sure init.d scripts still work. You can always launch it in the background from /etc/rc.local, with the usual warnings that that's not always a good idea.

NB: This script will put the adapter in 'discoverable mode' when started. By default that lasts for three minutes. You can change the default timeout in /etc/bluetooth/main.conf, and even set it to never timeout, BUT that effectively means that ANY device can pair with you, any time. Something of a security concern.

Code: Select all

#!/usr/bin/python

# blueagent5.py
# Dependencies: python-gobject (install with e.g. 'sudo apt-get install python-gobject' on Raspian
# Author: Douglas Otwell
# This software is released to the public domain

# The Software is provided "as is" without warranty of any kind, either express or implied, 
# including without limitation any implied warranties of condition, uninterrupted use, 
# merchantability, fitness for a particular purpose, or non-infringement.

import time
import sys
import dbus
import dbus.service
import dbus.mainloop.glib
import gobject
import logging
from optparse import OptionParser

SERVICE_NAME = "org.bluez"
AGENT_IFACE = SERVICE_NAME + '.Agent1'
ADAPTER_IFACE = SERVICE_NAME + ".Adapter1"
DEVICE_IFACE = SERVICE_NAME + ".Device1"
PLAYER_IFACE = SERVICE_NAME + '.MediaPlayer1'

LOG_LEVEL = logging.INFO
LOG_FILE = "/var/log/syslog"
#LOG_LEVEL = logging.DEBUG
#LOG_FILE = "/dev/stdout"
LOG_FORMAT = "%(asctime)s %(levelname)s [%(module)s] %(message)s"

"""Utility functions from bluezutils.py"""
def getManagedObjects():
    bus = dbus.SystemBus()
    manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.freedesktop.DBus.ObjectManager")
    return manager.GetManagedObjects()

def findAdapter():
    objects = getManagedObjects();
    bus = dbus.SystemBus()
    for path, ifaces in objects.iteritems():
        adapter = ifaces.get(ADAPTER_IFACE)
        if adapter is None:
            continue
        obj = bus.get_object(SERVICE_NAME, path)
        return dbus.Interface(obj, ADAPTER_IFACE)
    raise Exception("Bluetooth adapter not found")
    

class BlueAgent(dbus.service.Object):
    AGENT_PATH = "/blueagent5/agent"
    CAPABILITY = "DisplayOnly"
    pin_code = None

    def __init__(self, pin_code):
        dbus.service.Object.__init__(self, dbus.SystemBus(), BlueAgent.AGENT_PATH)
        self.pin_code = pin_code

        logging.basicConfig(filename=LOG_FILE, format=LOG_FORMAT, level=LOG_LEVEL)
        logging.info("Starting BlueAgent with PIN [{}]".format(self.pin_code))
        
    @dbus.service.method(AGENT_IFACE, in_signature="os", out_signature="")
    def DisplayPinCode(self, device, pincode):
        logging.debug("BlueAgent DisplayPinCode invoked")

    @dbus.service.method(AGENT_IFACE, in_signature="ouq", out_signature="")
    def DisplayPasskey(self, device, passkey, entered):
        logging.debug("BlueAgent DisplayPasskey invoked")

    @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="s")
    def RequestPinCode(self, device):
        logging.info("BlueAgent is pairing with device [{}]".format(device))
        self.trustDevice(device)
        return self.pin_code

    @dbus.service.method(AGENT_IFACE, in_signature="ou", out_signature="")
    def RequestConfirmation(self, device, passkey):
        """Always confirm"""
        logging.info("BlueAgent is pairing with device [{}]".format(device))
        self.trustDevice(device)
        return

    @dbus.service.method(AGENT_IFACE, in_signature="os", out_signature="")
    def AuthorizeService(self, device, uuid):
        """Always authorize"""
        logging.debug("BlueAgent AuthorizeService method invoked")
        return

    @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="u")
    def RequestPasskey(self, device):
        logging.debug("RequestPasskey returns 0")
        return dbus.UInt32(0)

    @dbus.service.method(AGENT_IFACE, in_signature="o", out_signature="")
    def RequestAuthorization(self, device):
        """Always authorize"""
        logging.info("BlueAgent is authorizing device [{}]".format(self.device))
        return

    @dbus.service.method(AGENT_IFACE, in_signature="", out_signature="")
    def Cancel(self):
        logging.info("BlueAgent pairing request canceled from device [{}]".format(self.device))

    def trustDevice(self, path):
        bus = dbus.SystemBus()
        device_properties = dbus.Interface(bus.get_object(SERVICE_NAME, path), "org.freedesktop.DBus.Properties")
        device_properties.Set(DEVICE_IFACE, "Trusted", True)

    def registerAsDefault(self):
        bus = dbus.SystemBus()
        manager = dbus.Interface(bus.get_object(SERVICE_NAME, "/org/bluez"), "org.bluez.AgentManager1")
        manager.RegisterAgent(BlueAgent.AGENT_PATH, BlueAgent.CAPABILITY)
        manager.RequestDefaultAgent(BlueAgent.AGENT_PATH)

    def startPairing(self):
        bus = dbus.SystemBus()
        adapter_path = findAdapter().object_path
        adapter = dbus.Interface(bus.get_object(SERVICE_NAME, adapter_path), "org.freedesktop.DBus.Properties")
        adapter.Set(ADAPTER_IFACE, "Discoverable", True)
        
        logging.info("BlueAgent is waiting to pair with device")
        
bus = None

if __name__ == "__main__":
    pin_code = "0000"
    parser = OptionParser()
    parser.add_option("-p", "--pin", action="store", dest="pin_code", help="PIN code to pair with", metavar="PIN")
    (options, args) = parser.parse_args()

    # use the pin code if provided
    if (options.pin_code):
        pin_code = options.pin_code

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    agent = BlueAgent(pin_code)
    agent.registerAsDefault()
    agent.startPairing()

    mainloop = gobject.MainLoop()
    mainloop.run()

erotavlas
Posts: 41
Joined: Wed Mar 11, 2015 5:26 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Tue May 05, 2015 3:49 pm

Hi Douglas,
thank you very much for your help. On my system I have to install 'sudo apt-get install python-gobject python-dbus', then I tried the blueagent5.py python script, but it works only sometimes. In particular, with 'sudo blueagent5.py --pin 1234' I can pair to raspbian, but my devices sometimes cannot connect to it (they can connect for some milliseconds and disconnect immediately, I tried both with S2 and S4). Moreover, also in Jessie I can use init.d since I followed exactly your guide for installing pulseaudio 5 and bluex 5.23 and you used init.d.
I employed the script found here http://blog.scphillips.com/posts/2013/0 ... e-on-boot/ to write a daemon for python (the script is similar to your).

Code: Select all

#!/bin/sh

### BEGIN INIT INFO
# Provides:          myscript
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Starts myscript daemons
# Description: Starts myscript daemons
### END INIT INFO

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Description of the service"
NAME=blueagent5
DAEMON=/usr/sbin/$NAME.py
# options for daemon
DAEMON_ARGS=""
# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Root generally not recommended but necessary if Raspberry Pi GPIO are used from Python.
DAEMON_USER=root

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

do_start () {
	log_daemon_msg "Starting system $NAME daemon"
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_ARGS
    log_end_msg $?
}
do_stop () {
	log_daemon_msg "Stopping system $NAME daemon"
    start-stop-daemon --stop --pidfile $PIDFILE --retry 10
    log_end_msg $?
}

case "$1" in

	start|stop)
	    do_${1}
        ;;

    restart|reload|force-reload)
        do_stop
        do_start
        ;;

    status)
        status_of_proc "$NAME" "$DAEMON" && exit 0 || exit $?
        ;;

    *)
    	echo "Usage: $SCRIPTNAME {start|stop|restart|status}"
        exit 1
    ;;

esac
exit 0
and then

Code: Select all

sudo chmod +x /usr/sbin/blueagent5.py
sudo chmod +x /etc/init.d/blueagent5
sudo update-rc.d blueagent5 defaults
Last edited by erotavlas on Wed May 06, 2015 11:07 am, edited 5 times in total.

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Tue May 05, 2015 4:09 pm

Perhaps I misunderstood what you were looking for. The blueagent5 script only handles pairing requests and has nothing to do with making or accepting connections. Your streaming device and PulseAudio should handle the connections. Are you saying you can connect if you have manually paired, but not if you paired with blueagent5?

erotavlas
Posts: 41
Joined: Wed Mar 11, 2015 5:26 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Tue May 05, 2015 4:46 pm

Hi,
I'm looking for something that automates the process of pairing and of connection to devices i.e. the procedure that I have to do manually from the command line of bluetoothctl as described in your guide or in my previous post.
Now, with your script I can see raspberry as available device and I can pair to it, but is does not provide to me the PIN code for the connection step. Sometimes I can connect without providing a PIN and sometimes I cannot connect.
Where is my mistake?

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Wed May 06, 2015 3:50 am

Ok, in a nutshell, here are the steps for streaming AD2P (Bluetooth) audio to the Raspberry Pi.

1. First the devices must be paired and trusted to each other. This step creates keys on each device so that they can trust each other in the future. This step only needs to be done ONCE for each pair of devices, generally. On my headless systems, I use SSH and bluetoothctl for this step, since it's only needed once, or rarely.

2. The streaming device requests a connection (say, by opening the bluetooth settings on your phone and tapping 'connect'). On the Pi, PulseAudio (using module-bluetooth-discover) accepts the connection and creates a 'source'. This is handled by the PulseAudio daemon automagically.

3. That 'source' then needs to be patched to a PulseAudio 'sink'. That's the job of the udev script I posted in the guide. When PulseAdio creates the sink, the OS sees that and calls the udev script. The script uses module-loopback to connect the sink and source. (There is also a way to let PulseAudio do the loopback, using module-bluetooth-policy. I haven't tried it, but a poster on this thread reported success.)

So, once the software is installed, PulseAudio and Bluez are started, the udev script is installed, and the devices are paired and trusted, everything should run without intervention.

IF you frequently need to pair with different devices (say, you want to let your friends connect their phones, and you have a lot of friends), you can run a pairing agent like Bluetooth Manager (requires a GUI), or blueagent5. The latter will automatically accept all pairing requests. Depending on the capabilities of your dongle, that may or may not require a PIN. If asked, blueagent5 will respond with the PIN specified on the command-line, or '0000'.

Connecting and immediately disconnecting sounds like your device is not trusted. Use the 'bluetoothctl info AA:BB:CC:DD:EE:FF' to make sure your device is both paired and trusted. If not, use 'trust AA:BB:CC:DD:EE:FF' .
Blueagent5 will trust the device automatically, but only if it wasn't paired to begin with. To test blueagent5, you should 'forget' the Pi on your phone, and 'remove' the phone on the Pi with bluetoothctl. Also, only one pairing agent may be running at a time. Don't try to use the bluetoothctl agent and blueagent5 at the same time.

I've tested blueagent5 pretty thouroughly, but only under Wheezy, which was the point of this thread, after all. The Jessie distro includes BlueZ 5 and PulseAudio 5, so almost all of that guide is pointless on Jessie. You can simply apt-get install them.

DavidWeinberg
Posts: 3
Joined: Wed May 06, 2015 2:46 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Wed May 06, 2015 2:50 pm

Hi Douglas6,
I tried ti run your code and got this error:

Code: Select all

[email protected] ~ $ sudo python blueagent5.py --pin 1234
Traceback (most recent call last):
  File "blueagent5.py", line 139, in <module>
    agent.registerAsDefault()
  File "blueagent5.py", line 113, in registerAsDefault
    manager.RegisterAgent(BlueAgent.AGENT_PATH, BlueAgent.CAPABILITY)
  File "/usr/lib/python2.7/dist-packages/dbus/proxies.py", line 70, in __call__
    return self._proxy_method(*args, **keywords)
  File "/usr/lib/python2.7/dist-packages/dbus/proxies.py", line 145, in __call__
    **keywords)
  File "/usr/lib/python2.7/dist-packages/dbus/connection.py", line 651, in call_blocking
    message, timeout)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.UnknownMethod: Method "RegisterAgent" with signature "ss" on interface "org.bluez.AgentManager1" doesn't exist
Maybe you have idea aboout this?

Thanks, David.

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Wed May 06, 2015 4:08 pm

Yup. That code is written for BlueZ 5. You're running BlueZ 4.

DavidWeinberg
Posts: 3
Joined: Wed May 06, 2015 2:46 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Wed May 06, 2015 4:15 pm

And what I need to update to blues 5?

Great thanks, David.

texy
Forum Moderator
Forum Moderator
Posts: 5160
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: [Guide] Pulseaudio 5 on Wheezy

Wed May 06, 2015 5:02 pm

DavidWeinberg wrote:And what I need to update to blues 5?

Great thanks, David.
Please read the thread - first post show you how.
Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Wed May 06, 2015 7:10 pm

You can find similar code for BlueZ 4 here: https://github.com/Douglas6/pinaple/blo ... aple-agent.

DavidWeinberg
Posts: 3
Joined: Wed May 06, 2015 2:46 pm

Re: [Guide] Pulseaudio 5 on Wheezy

Sun May 10, 2015 12:39 pm

Douglas6 wrote:You can find similar code for BlueZ 4 here: https://github.com/Douglas6/pinaple/blo ... aple-agent.
Great Thanks. It's solved my problem.
David.

deivid
Posts: 46
Joined: Thu Oct 23, 2014 7:08 am

Re: [Guide] Pulseaudio 5 on Wheezy

Tue May 12, 2015 7:16 am

I'm trying to cross compile ffmpeg with libpulse support and I thought this might be the best place to ask. I've already built a static binary with all the codecs I need, but it uses alsa. To enable pulse I added "--enable-libpulse" to the configure command, but I keep getting

Code: Select all

ERROR: libpulse not found using pkg-config
When libpulse0 and libpulse0-dev are installed on the host (x86 system). any clue?

texy
Forum Moderator
Forum Moderator
Posts: 5160
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: [Guide] Pulseaudio 5 on Wheezy

Thu May 14, 2015 6:44 pm

Douglas6 wrote:Here's the Python code I have come up with. It's very much in the alpha stage, so I'll simply post it here; the next version I'll try and put on Github.

Some background: Since BlueZ 4, the recommended way to communicate with BlueZ is via D-Bus. This works quite well, and is easy to do in Python, so it's the approach I took. This code (blueplayer) connects to the system bus, and waits for propertyChanged 'signals' from BlueZ on the bus, signifying changes to the state of the BlueZ device; either status changes, or changes to the current track. It will update it's state, and then update the display. I'm using Adafruit's 16x2 RGB Pi plate with buttons; code changes will be needed for a different display and navigation buttons.

Blueplayer first checks if a MediaPlayer1 is active, if not, it waits for one. It also acts as a pairing agent so you can pair new devices (currently any pairing request is accepted; that's a to-do). Then it just waits on changes and reacts accordingly. Changes include a change to "Track", from which the "Title" and "Artist" are updated and displayed. Other changes to the device state (idle, disconnected) will turn off the display's backlight.

Dependencies: You'll probably need to

Code: Select all

sudo apt-get install python-gobject python-smbus
Usage: Run 'sudo blueplayer' (root permission is required to access the system bus). Press the 'Select' button (far left) to make the Pi discoverable. You should be able to pair from your device. Use the 'up' button to pause/unpause. Use the 'left' button for previous, and the 'right' button for next or skip.

The code follows. I hope it is useful.
blueplayer

Code: Select all

#!/usr/bin/env python

# Dependencies:
# sudo apt-get install -y python-gobject
# sudo apt-get install -y python-smbus

import time
import signal
import dbus
import dbus.service
import dbus.mainloop.glib
import gobject
import logging
from lcd import Lcd

SERVICE_NAME = "org.bluez"
AGENT_IFACE = SERVICE_NAME + '.Agent1'
ADAPTER_IFACE = SERVICE_NAME + ".Adapter1"
DEVICE_IFACE = SERVICE_NAME + ".Device1"
PLAYER_IFACE = SERVICE_NAME + '.MediaPlayer1'
TRANSPORT_IFACE = SERVICE_NAME + '.MediaTransport1'

#LOG_LEVEL = logging.INFO
LOG_LEVEL = logging.DEBUG
LOG_FILE = "/dev/stdout"
LOG_FORMAT = "%(asctime)s %(levelname)s %(message)s"

"""Utility functions from bluezutils.py"""
def getManagedObjects():
    bus = dbus.SystemBus()
    manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.freedesktop.DBus.ObjectManager")
    return manager.GetManagedObjects()

def findAdapter():
    objects = getManagedObjects();
    bus = dbus.SystemBus()
    for path, ifaces in objects.iteritems():
        adapter = ifaces.get(ADAPTER_IFACE)
        if adapter is None:
            continue
        obj = bus.get_object(SERVICE_NAME, path)
        return dbus.Interface(obj, ADAPTER_IFACE)
    raise Exception("Bluetooth adapter not found")

class BluePlayer(dbus.service.Object):
    AGENT_PATH = "/blueplayer/agent"
    CAPABILITY = "DisplayOnly"

    lcd = None
    bus = None
    adapter = None
    device = None
    deviceAlias = None
    player = None
    transport = None
    connected = None
    state = None
    status = None
    discoverable = None
    track = None

    def __init__(self, lcd):
        """Initialize gobject, start the LCD, and find any current media players"""
        self.lcd = lcd
        self.bus = dbus.SystemBus()

        dbus.service.Object.__init__(self, dbus.SystemBus(), BluePlayer.AGENT_PATH)

        self.bus.add_signal_receiver(self.playerHandler,
                bus_name="org.bluez",
                dbus_interface="org.freedesktop.DBus.Properties",
                signal_name="PropertiesChanged",
                path_keyword="path")

        self.registerAgent()

        adapter_path = findAdapter().object_path
        self.bus.add_signal_receiver(self.adapterHandler,
                bus_name = "org.bluez",
                path = adapter_path,
                dbus_interface = "org.freedesktop.DBus.Properties",
                signal_name = "PropertiesChanged",
                path_keyword = "path")


        self.findPlayer()
        self.updateDisplay()

    def start(self):
        """Start the BluePlayer by running the gobject mainloop()"""
        try:
            mainloop = gobject.MainLoop()
            mainloop.run()
        except:
            self.end()

    def findPlayer(self):
        """Find any current media players and associated device"""
        manager = dbus.Interface(self.bus.get_object("org.bluez", "/"), "org.freedesktop.DBus.ObjectManager")
        objects = manager.GetManagedObjects()

        player_path = None
        transport_path = None
        for path, interfaces in objects.iteritems():
            if PLAYER_IFACE in interfaces:
                player_path = path
            if TRANSPORT_IFACE in interfaces:
                transport_path = path

        if player_path:
            logging.debug("Found player on path [{}]".format(player_path))
            self.connected = True
            self.getPlayer(player_path)
            player_properties = self.player.GetAll(PLAYER_IFACE, dbus_interface="org.freedesktop.DBus.Properties")
            if "Status" in player_properties:
                self.status = player_properties["Status"]
            if "Track" in player_properties:
                self.track = player_properties["Track"]
        else:
            logging.debug("Could not find player")

        if transport_path:
            logging.debug("Found transport on path [{}]".format(player_path))
            self.transport = self.bus.get_object("org.bluez", transport_path)
            logging.debug("Transport [{}] has been set".format(transport_path))
            transport_properties = self.transport.GetAll(TRANSPORT_IFACE, dbus_interface="org.freedesktop.DBus.Properties")
            if "State" in transport_properties:
                self.state = transport_properties["State"]

    def getPlayer(self, path):
        """Get a media player from a dbus path, and the associated device"""
        self.player = self.bus.get_object("org.bluez", path)
        logging.debug("Player [{}] has been set".format(path))
        device_path = self.player.Get("org.bluez.MediaPlayer1", "Device", dbus_interface="org.freedesktop.DBus.Properties")
        self.getDevice(device_path)

    def getDevice(self, path):
        """Get a device from a dbus path"""
        self.device = self.bus.get_object("org.bluez", path)
        self.deviceAlias = self.device.Get(DEVICE_IFACE, "Alias", dbus_interface="org.freedesktop.DBus.Properties")

    def playerHandler(self, interface, changed, invalidated, path):
        """Handle relevant property change signals"""
        logging.debug("Interface [{}] changed [{}] on path [{}]".format(interface, changed, path))
        iface = interface[interface.rfind(".") + 1:]

        if iface == "Device1":
            if "Connected" in changed:
                self.connected = changed["Connected"]
        if iface == "MediaControl1":
            if "Connected" in changed:
                self.connected = changed["Connected"]
                if changed["Connected"]:
                    logging.debug("MediaControl is connected [{}] and interface [{}]".format(path, iface))
                    self.findPlayer()
        elif iface == "MediaTransport1":
            if "State" in changed:
                logging.debug("State has changed to [{}]".format(changed["State"]))
                self.state = (changed["State"])
            if "Connected" in changed:
                self.connected = changed["Connected"]
        elif iface == "MediaPlayer1":
            if "Track" in changed:
                logging.debug("Track has changed to [{}]".format(changed["Track"]))
                self.track = changed["Track"]
            if "Status" in changed:
                logging.debug("Status has changed to [{}]".format(changed["Status"]))
                self.status = (changed["Status"])

        self.updateDisplay()

    def updateDisplay(self):
        """Display the current status of the device on the LCD"""
        logging.debug("Updating display for connected: [{}]; state: [{}]; status: [{}]; discoverable [{}]".format(self.connected, self.state, self.status, self.discoverable))
        if self.discoverable:
            self.wake()
            self.showDiscoverable()
        else:
            if self.connected:
                if self.state == "idle":
                    self.sleep()
                else:
                    self.wake()
                    if self.status == "paused":
                        self.showPaused()
                    else:
                        self.showTrack()
            else:
                self.sleep()

    def showDevice(self):
        """Display the device connection info on the LCD"""
        self.lcd.clear()
        self.lcd.writeLn("Connected to:", 0)
        self.lcd.writeLn(self.deviceAlias, 1)
        time.sleep(2)

    def showTrack(self):
        """Display track info on the LCD"""
        lines = []
        if "Artist" in self.track:
                    lines.append(self.track["Artist"])
                    if self.track["Title"]:
                         lines.append(self.track["Title"])
        elif "Title" in self.track:
            lines = self.lcd.wrap(self.track["Title"])

        self.lcd.clear()
        for i, line in enumerate(lines):
            if i >= self.lcd.numlines: break
            self.lcd.writeLn(lines[i], i)

    def showPaused(self):
        self.lcd.clear()
        self.lcd.writeLn("Device is paused", 0)

    def showDiscoverable(self):
        self.lcd.clear()
        self.lcd.writeLn("Waiting to pair", 0)
        self.lcd.writeLn("with device", 1)


    def next(self):
        self.player.Next(dbus_interface=PLAYER_IFACE)

    def previous(self):
        self.player.Previous(dbus_interface=PLAYER_IFACE)

    def play(self):
        self.player.Play(dbus_interface=PLAYER_IFACE)

    def pause(self):
        self.player.Pause(dbus_interface=PLAYER_IFACE)

    def volumeUp(self):
        self.control.VolumeUp(dbus_interface=CONTROL_IFACE)
        self.transport.VolumeUp(dbus_interface=TRANSPORT_IFACE)

    def wake(self):
        """Wake up the LCD"""
        self.lcd.backlight(Lcd.TEAL)

    def shutdown(self):
        self.lcd.end()

    def sleep(self):
        """Put the LCD to sleep"""
        self.lcd.clear()
        self.lcd.backlight(Lcd.OFF)

    def getStatus(self):
        return self.status

    """Pairing agent methods"""
    @dbus.service.method(AGENT_IFACE, in_signature="ou", out_signature="")
    def RequestConfirmation(self, device, passkey):
        """Always confirm"""
        logging.debug("RequestConfirmation returns")
        self.trustDevice(device)
        return

    @dbus.service.method(AGENT_IFACE, in_signature="os", out_signature="")
    def AuthorizeService(self, device, uuid):
        """Always authorize"""
        logging.debug("Authorize service returns")
        return

    def trustDevice(self, path):
        """Set the device to trusted"""
        device_properties = dbus.Interface(self.bus.get_object(SERVICE_NAME, path), "org.freedesktop.DBus.Properties")
        device_properties.Set(DEVICE_IFACE, "Trusted", True)

    def registerAgent(self):
        """Register BluePlayer as the default agent"""
        manager = dbus.Interface(self.bus.get_object(SERVICE_NAME, "/org/bluez"), "org.bluez.AgentManager1")
        manager.RegisterAgent(BluePlayer.AGENT_PATH, BluePlayer.CAPABILITY)
        manager.RequestDefaultAgent(BluePlayer.AGENT_PATH)
        logging.debug("Blueplayer is registered as a default agent")

    def startPairing(self):
        logging.debug("Starting to pair")
        """Make the adpater discoverable"""
        adapter_path = findAdapter().object_path
        adapter = dbus.Interface(self.bus.get_object(SERVICE_NAME, adapter_path), "org.freedesktop.DBus.Properties")
        adapter.Set(ADAPTER_IFACE, "Discoverable", True)

def navHandler(buttons):
    logging.debug("Handling navigation for [{}]".format(buttons))
    """Handle the navigation buttons"""
    if buttons == Lcd.BUTTON_SELECT:
        player.startPairing()
    elif buttons == Lcd.BUTTON_LEFT:
        player.previous()
    elif buttons == Lcd.BUTTON_RIGHT:
        player.next()
    elif buttons == Lcd.BUTTON_UP:
        if player.getStatus() == "playing":
            player.pause()
        else:
            player.play()

logging.basicConfig(filename=LOG_FILE, format=LOG_FORMAT, level=LOG_LEVEL)
logging.info("Starting BluePlayer")

gobject.threads_init()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

lcd = Lcd()
lcd.begin(16, 2, handler=navHandler)
lcd.backlight(Lcd.TEAL)
lcd.clear()
lcd.writeLn("Starting", 0)
lcd.writeLn("BluePlayer", 1)
time.sleep(2)

player = None
try:
    player = BluePlayer(lcd)
    mainloop = gobject.MainLoop()
    mainloop.run()
except KeyboardInterrupt as ex:
    logging.info("BluePlayer canceled by user")
except Exception as ex:
    logging.error("How embarrassing. The following error occurred {}".format(ex))
finally:
    if player:
        player.shutdown()
And here is the Lcd code that provides basic display functions based on the Adafruit LCD plate, and button polling:

Code: Select all

from threading import Thread
import time
from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate

class Lcd(Adafruit_CharLCDPlate):
    BUTTON_SELECT = 1
    BUTTON_LEFT = 16
    BUTTON_RIGHT = 2
    BUTTON_UP = 8
    BUTTON_DOWN = 4

    worker = None
    polling = True
    handler = None

    def begin(self, cols, rows, handler=None):
        super(Lcd, self).begin(cols, rows)
        self.handler = handler
        if self.handler:
            self.worker = Thread(target=self.getButtons)
            self.worker.start()

    def end(self):
        self.polling = False;
        self.clear()
        self.backlight(Lcd.OFF)
        self.stop()

    def writeLn(self, str, row):
        self.setCursor(0, row)
        self.message(self.replaceAccents(str[:40].ljust(40)))

    def replaceAccents(self, str):
        return str.replace(u"\xe9", "e")

    def getButtons(self):
        button_cache = 0;
        while self.polling:
            test = self.buttons()
            buttons = test - button_cache;
            button_cache = test if test > 0 else 0
            if buttons > 0:
                self.handler(buttons)
            time.sleep(0.1)

    def wrap(self, str):
        lines = [];
        while len(str) > self.numcols:
            idx = str[:self.numcols+1].rfind(" ")
            if idx > 0:
                lines.append(str[:idx])
                str = str[idx+1:].lstrip()
            else:
                lines.append(str[:self.numcols])
                str = str[self.numcols:].lstrip()

        lines.append(str)

        return  lines



if __name__ == "__main__":
    def handleNav(buttons):
        print(buttons)

    try:
        lcd = Lcd()
        lcd.begin(16, 2, handler=handleNav)

        while True:
            time.sleep(1)
    except:
        lcd.end()
Image

....after taking a very long break from this, I took another look ;)

I followed the installation steps from this post :
viewtopic.php?f=29&t=87138#p619713
but installed bluez 5.30 instead of bluez 5.23.
One problem I have always had is with pairing, perhaps it's an iphone thing, but I could never do it via CLI, so previously I have needed to install blueman and pair via X.
However blueman doesn't work with later versions of raspbian wheezy, and your instructions for using bluetoothctl did not bring up the pairing sequencem although the iphone was discovered, I could not 'see' the pi from the iphone. What I did find that worked was to use

Code: Select all

agent DisplayYesNo
instead of

Code: Select all

agent on
Now I find that it brings up the pairing dialogue and I can continue to trust my iphone, etc. The system now fully works, and even the python code above brings up the track artist and song, just want I wanted. However............
the audio quality is full of cracks and pops, and also the volume is not controllable from the iphone......
Ill have to investigate further.....

Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

texy
Forum Moderator
Forum Moderator
Posts: 5160
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: [Guide] Pulseaudio 5 on Wheezy

Thu May 14, 2015 7:41 pm

I just tried a usb pcm2704 sound card and still the same symptoms - crackles and unable to control volume from iphone :(

Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Thu May 14, 2015 8:21 pm

texy wrote:What I did find that worked was to use

Code: Select all

agent DisplayYesNo
instead of

Code: Select all

agent on
Now I find that it brings up the pairing dialogue and I can continue to trust my iphone, etc. The system now fully works, and even the python code above brings up the track artist and song, just want I wanted. However............
the audio quality is full of cracks and pops, and also the volume is not controllable from the iphone......
Ill have to investigate further.....
Great, glad to hear it's working, kinda. The difference in pairing may be due to your iPhone, or your dongle (different dongles pair differently, I've noticed.) I will add your tip to the original post for others, thanks.

I'm guessing the audio is only bad when streaning? Using paplay works adequately? I've been playing with BlueZ 5.30, but haven't tried A2DP with it. I'll try and load up PulseAudio and give it another try. I wonder if PA6 works any better,

texy
Forum Moderator
Forum Moderator
Posts: 5160
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: [Guide] Pulseaudio 5 on Wheezy

Thu May 14, 2015 9:04 pm

I didn't make any notes but I couldn't get PA6 to compile on wheezy.
Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

shawson
Posts: 6
Joined: Fri Jun 05, 2015 1:30 am

Re: [Guide] Pulseaudio 5 on Wheezy

Fri Jun 05, 2015 1:34 am

Hi- this is a great article - the only one infact that I could find that worked! Everything works perfectly infact unless I disconnect BT audio from the mobile and then try to reconnect - the syslog shows this;

Code: Select all

Jun  5 01:26:00 raspberrypi logger: [bluez-udev] Patching bluez_source.B4_3A_28_08_F7_9A into ALSA sink #0
Jun  5 01:26:00 raspberrypi pulseaudio[2233]: [pulseaudio] module-loopback.c: No such source.
Jun  5 01:26:00 raspberrypi pulseaudio[2233]: [pulseaudio] module.c: Failed to load module "module-loopback" (argument: "source=bluez_source.B4_3A_28_08_F7_9A sink=0"): initialization failed.
Jun  5 01:26:00 raspberrypi logger: [bluez-udev] PulseAudio module-loopback returned handle []
Jun  5 01:26:00 raspberrypi logger: [bluez-udev] Bluetooth device is being removed [B4:3A:28:08:F7:9A]
Jun  5 01:26:00 raspberrypi logger: [bluez-udev] Turning on bluetooth discovery
Jun  5 01:28:58 raspberrypi pulseaudio[2233]: [pulseaudio] module-loopback.c: No such source.
Jun  5 01:28:58 raspberrypi pulseaudio[2233]: [pulseaudio] module.c: Failed to load module "module-loopback" (argument: "source=bluez_source.B4_3A_28_08_F7_9A sink=0"): initialization failed.
Jun  5 01:29:02 raspberrypi pulseaudio[2233]: [pulseaudio] module-loopback.c: No such source.
Jun  5 01:29:02 raspberrypi pulseaudio[2233]: [pulseaudio] module.c: Failed to load module "module-loopback" (argument: "source=bluez_source.B4_3A_28_08_F7_9A sink=0"): initialization failed.
Rebooting fixes this, but any ideas what might be causing this/ how I could fix it properly?

Many thanks!

Shaw.

User avatar
Douglas6
Posts: 4800
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: [Guide] Pulseaudio 5 on Wheezy

Fri Jun 05, 2015 4:41 pm

My first guess is that it's a timing issue, and the udev rule is trying to connect the source before Pulse Audio has finished creating it. You could check this by doing a

Code: Select all

sudo pactl list sources
AFTER the disconnect and failed reconnect, to see if the source did eventually get created (look for a source named bluez_source.B4_3A_28_08_F7_9A.) If that's the case, try adding a 2-3 second delay in the udev script (/usr/local/bin/bluez-udev)

Code: Select all

.
.
.
if [ "$action" = "add" ]; then
    sleep(3) #ADD THIS LINE
    logger "[$(basename $0)] Bluetooth device is being added [$name]"
.
.
.
Option 2 is perhaps more elegant. Ditch the udev script entirely, by commenting out or removing the line you added to /etc/udev/rules.d/99-input.rules

Code: Select all

# KERNEL=="input[0-9]*", RUN+="/usr/local/bin/bluez-udev"
Then use the Pulse Audio module-bluetooth-policy to perform the loop-back. Simply add the following line to the bottom of /etc/pulse/system.pa

Code: Select all

load-module module-bluetooth-policy
and reboot.

Return to “Advanced users”