A2DP connection to headphones

Aug 27, 2010 at 1:57 AM
Edited Aug 27, 2010 at 4:16 AM

Hi,

Need to write app to connect, pair, and stream music to headphones or other audio device.  Would love to do it in C#.  Possible with this wrapper?  Pointers for someone who is a BT beginner?  I have the widcomm stack.  I have a dongle that supports a2dp and widcomm. 

Steve

 

Coordinator
Aug 27, 2010 at 6:57 AM

With this wrapper, you can currently list devices and pair devices. The class CManagedBtIf wraps native CBtIf clas. The class CHeadPhoneClient is not implemented but seems to be easy to wrap. You can find a C++ sample in the SDK and functions documentation in SDK PDF. You only need to wrap ConnectHeadphone function to attach A2DP device to your computer.

Nov 2, 2011 at 9:58 AM
Edited Nov 2, 2011 at 10:02 AM

Hi,

Thanks for your wrapper implementation!

I'm trying the same thing - I want to connect/disconnect bluetooth headphones in C# application. I honestly tried to user your code and wrap CHeadPhoneClient's ConnectHeadphone, but it always hangs when I call ConnectHeadphone. Could you please check if you can find what I did wrong?

First of all I have Widcomm stack, I can connect to headphones from Windows 7 bluetooth panel and it works. I can listen to music though the headphones. Your WidcommWrapper_Test also runs normally and finds my bluetooth headphones without a problem.

What I did is I've created new files in C++ project: CNativeHeadphoneClient.h/cpp and CManagedHeadphoneClient.h/cpp (code listings below).

In CmanagedBtIf.cs, in btn2_ShowInquiryWindow_Click(), when DialogResult is OK,  I have added a call to ConnectHeadphone():

Edited part of btn2_ShowInquiryWindow_Click():


Trace.TraceInformation("Device selected. Name: {0}, Adress: {1}, Connected: {2}",  frm.SelectedItem.BdName, frm.SelectedItem.DevAddress, frm.SelectedItem.BConnected);
Trace.TraceInformation(frm.SelectedItem.DevClass.ToString());
Trace.TraceInformation("Connecting to headphone...");
CG.Wrappers.Widcomm.CManagedHeadphoneClient wdHeadphone = new CG.Wrappers.Widcomm.CManagedHeadphoneClient();
wdHeadphone.ConnectHeadphone(frm.SelectedItem.DevAddress, "AV");
Trace.TraceInformation("OK!");

Basically the program just hangs on ConnectHeadphone() and does nothing. I am not sure what the "service name" should be. I tried null, "Stereo Audio" (as in Widcomm SDK example) and "AV".

 

 

Below are the files that I created for the C++ project (some linebreaks removed to be more compact):

CNativeHeadphoneClient.h:


#ifndef _CNativeHeadPhoneClient
#define _CNativeHeadPhoneClient
#define _AFXDLL
#include <afxwin.h>         // MFC core and standard components
#include <BtIfDefinitions.h>
#include <BtIfClasses.h>
#include <vcclr.h>
#include "CManagedHeadphoneClient.h"
namespace CG {
	namespace Wrappers {
		namespace Widcomm {
			ref class CManagedHeadphoneClient ;
			class CNativeHeadphoneClient : public CHeadphoneClient {
				public:
					CNativeHeadphoneClient(CManagedHeadphoneClient^ managedBtIf);
					virtual ~CNativeHeadphoneClient();
				private:
					//Reference to managed wrapper class. Usefull to wrap virtual native functions
					gcroot<CManagedHeadphoneClient^> mManagedHeadphoneClient;
			};
		}
	}
}
#endif

 

CNativeHeadphoneClient.cpp:


#include "CNativeHeadphoneClient.h"
namespace CG {
	namespace Wrappers {
		namespace Widcomm {
			CNativeHeadphoneClient::CNativeHeadphoneClient(CManagedHeadphoneClient^ managedHeadphoneClient) {
				this->mManagedHeadphoneClient = managedHeadphoneClient; 
			}
			CNativeHeadphoneClient::~CNativeHeadphoneClient() {
			}
		}
	}
} 

CManagedHeadphoneClient.h:


#ifndef _CManagedHeadphoneClient
#define _CManagedHeadphoneClient
#define _AFXDLL
#include <afxwin.h>         // MFC core and standard components
#include "Enums.h"
#include "DeviceAddress.h"
#include "DeviceClass.h"
#include "DeviceInfo.h"
#include "DeviceVersionInfo.h"
#include "ServiceClass.h"
#include <BtIfDefinitions.h>
#include <BtIfClasses.h>
#include "CNativeHeadphoneClient.h"
#include "CManagedSdpDiscoveryRec.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace CG::Wrappers::Widcomm::Datatypes;
#using <mscorlib.dll>
namespace CG {
	namespace Wrappers {
		namespace Widcomm {
			class CNativeHeadphoneClient;
			/// <summary>
			/// Provides interface level management functions, e.g., methods for doing inquiry and service discovery
			/// </summary>
			public ref class CManagedHeadphoneClient sealed {
				public:
					/// <summary>
					/// This public constructor instantiate native Widcomm HeadphoneClient class
					/// </summary>
					CManagedHeadphoneClient();
					/// <summary>
					/// This public destructor free unmanaged ressources
					/// </summary>
					~CManagedHeadphoneClient();
					/// <summary>
					/// This function connects to headphone
					/// </summary>
					void ConnectHeadphone(DeviceAddress^ bda, System::String^ serviceName);
					/// <summary>
					/// This function disconnects from headphone
					/// </summary>
					void DisconnectHeadphone(long Handle);
				private:
					// Reference to native HeadphoneClient class
					CNativeHeadphoneClient* _mWidcommStack;
					//Free native wdStack resources
					!CManagedHeadphoneClient();
			};
		}
	}
}
#endif 

CManagedHeadphoneClient.cpp:


#include "CManagedHeadphoneClient.h"
namespace CG {
	namespace Wrappers {
		namespace Widcomm {
			CManagedHeadphoneClient::CManagedHeadphoneClient() {
				_mWidcommStack = new CNativeHeadphoneClient(this);
			}
			CManagedHeadphoneClient::~CManagedHeadphoneClient() {
				this->!CManagedHeadphoneClient();
			}
			CManagedHeadphoneClient::!CManagedHeadphoneClient() {
				delete (_mWidcommStack);
			}
			void CManagedHeadphoneClient::ConnectHeadphone(DeviceAddress^ deviceAddress, System::String^ serviceName) {
				pin_ptr<System::Byte> pDeviceAddress = &(deviceAddress->Address[0]);
				char* _pServiceName = (char*)Marshal::StringToHGlobalAnsi(serviceName).ToPointer();
				_mWidcommStack->ConnectHeadphone(pDeviceAddress, _pServiceName);
			}
			void CManagedHeadphoneClient::DisconnectHeadphone(long Handle) {
				_mWidcommStack->DisconnectHeadphone(Handle);
			}
		}
	}
}


Thanks for your help!

-Timo

Coordinator
Nov 5, 2011 at 3:39 PM

Hi Timo

 

I'm working on your issue. Can I integrate your code in wrapper source code and publish it?

Nov 5, 2011 at 4:45 PM

Hello Gregg

Thanks for your response. Sure you can integrate and publish.

I am mainly working with low level C and high level C#, so my C++ code is written (or rather copy pasted) without any deeper understanding how it should be done! Beware! :)

Coordinator
Nov 6, 2011 at 12:50 PM

I've integrated your code.

I've just added RegStatusChangeCB callback subscription in CNativeHeadphoneClient class and some buttons in test application. The code works fine on my computer.

You have to click on 'Initialize' button to init CManagedHeadphoneClient class and after that, you can click on Connect or Disconnect buttons (you have to enter your headphone address to connect and a handle to disconnect). Can you try on your computer?

Beware, I have updated solution to visual studio 2010 and .NET 4

Changes available at: http://widcommwrapper.codeplex.com/SourceControl/changeset/changes/70842

Nov 6, 2011 at 7:42 PM

Thanks for update Gregg.

Unfortunately it still hangs on my side when I click Connect.

In the beginning everything seems to be working fine. I check some preliminary stuff, I look for BT devices, it finds my Logitech wireless audio, I stop inquiry, I initialize headphone...

But once I enter headphone address (C8:84:47:02:89:8C) and press Connect, the program just hangs, just as it did with the old version. In Visual Studio debug Output window I can see the following:

WidcommWrapper2.vshost.exe Information: 0 : IsDllPresent() returns: True
WidcommWrapper2.vshost.exe Information: 0 : IsStackServerUp() returns: True
WidcommWrapper2.vshost.exe Information: 0 : IsDeviceReady() returns: True
WidcommWrapper2.vshost.exe Information: 0 : Start inquiry
WidcommWrapper2.vshost.exe Information: 0 : Start inquiry status: True
The thread '<No Name>' (0x2004) has exited with code 0 (0x0).
WidcommWrapper2.vshost.exe Information: 0 : Device name: (8C)Logitech Adapter, Device addr: C8:84:47:02:89:8C, Connected: False
WidcommWrapper2.vshost.exe Information: 0 : Service class: Rendering, ObjectAudio, MajorDeviceClass: Audio, MinorDeviceClass: AudioUnclassified or ImagingUnclassified or PhoneUnclassified or CompUnclassified

WidcommWrapper2.vshost.exe Information: 0 : Device name: Nokia C5-00, Device addr: C8:DF:7C:EA:B8:DE, Connected: False
WidcommWrapper2.vshost.exe Information: 0 : Service class: Networking, Capturing, ObjectTransfer, ObjectTelephony, MajorDeviceClass: Phone, MinorDeviceClass: PhoneSmart or CompLaptop

WidcommWrapper2.vshost.exe Information: 0 : Inquiry stopped
WidcommWrapper2.vshost.exe Information: 0 : Headphone client initialized
The thread '<No Name>' (0x14a8) has exited with code 0 (0x0).
The thread '<No Name>' (0x1fac) has exited with code 0 (0x0).

 

 

If I break the execution, the program is stopped at:

return (Enums::eHeadphoneReturnCode)this->_mHeadphoneClient->ConnectHeadphone(pDeviceAddress, _pServiceName);

 

in CManagedHeadphoneClient.cpp.

 

I am on Windows 7 Professional x64, using Visual Studio 2010 Ultimate. Got latest Widcomm BT DK installed. My headphone works fine when I connect it using Windows BT Devices control panel.

I'm using A-LINK bluetooth dongle:

Broadcom 2046 Bluetooth 2.1+EDR USB Dongle with First Connect

Driver date: 8.2.2008

Driver version: 6.1.0.4100

 

I'm stuck here. Any idea how should I debug this problem?

Coordinator
Nov 6, 2011 at 8:11 PM

Can you try to press initialize button (to subscribe HeadphoneStatusChanged event) and connect/deconnect your headphone in Windows control panel. You should see notifications in sample application:

On my side:
WidcommWrapper_Test.exe Information: 0 : Connect headphone result: Success
WidcommWrapper_Test.exe Information: 0 : Headphone client status changed => Address: 00:1A:80:4E:F7:1C / Name: DR-BT21G / Handle: 4363 / Status: -2130706015
WidcommWrapper_Test.exe Information: 0 : Headphone client status changed => Address: 00:1A:80:4E:F7:1C / Name: DR-BT21G / Handle: 4382 / Status: -2130706015
WidcommWrapper_Test.exe Information: 0 : Headphone client status changed => Address: 00:1A:80:4E:F7:1C / Name: DR-BT21G / Handle: 4363 / Status: Connected
WidcommWrapper_Test.exe Information: 0 : Headphone client status changed => Address: 00:1A:80:4E:F7:1C / Name: DR-BT21G / Handle: 4360 / Status: Connected

Nov 6, 2011 at 8:59 PM

Okay. So I opened WidcommWrapper_Test and initialized headphone. Then connected and later disconnected headphone over Windows control panel. Got the following:

WidcommWrapper2.vshost.exe Information: 0 : Headphone client initialized
WidcommWrapper2.vshost.exe Information: 0 : Headphone client status changed => Address: C8:84:47:02:89:8C / Name: (8C)Logitech Adapter / Handle: 4363 / Status: Connected
The thread '<No Name>' (0x1854) has exited with code 0 (0x0).
WidcommWrapper2.vshost.exe Information: 0 : Headphone client status changed => Address: C8:84:47:02:89:8C / Name: (8C)Logitech Adapter / Handle: 4363 / Status: -2130706015

 

And well, I just tried to connect to headphone via WidcommWrapper_Test and it worked! This time I didn't run inquery (previously I've always run inquery before trying to connect to headphone).

 

It only hangs if I run inquery first. For example, if I open WidcommWrapper_Test, start inquery, wait for results, stop inquery, close WidcommWrapper. Then open control panel, press Connect and control panel freezes.

Every time I run inquery in WidcommWrapper_Test, afterwards I have to terminate Bluetooth Stack COM Server from Task Manager in order to get bluetooth working again. So my guess would be something is not finished properly during that inquery.

Otherwise connecting and disconnecting headphone seems to work fine!