1/*
2 * Copyright 2003-2013, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		J��r��me Duval
7 *		Fran��ois Revol
8 *		Axel D��rfler, axeld@pinc-software.de.
9 *		Puck Meerburg, puck@puckipedia.nl
10 *		Dario Casalinuovo, b.vitruvio@gmail.com
11 */
12
13
14#include "MixerControl.h"
15
16#include <string.h>
17
18#include <Debug.h>
19#include <ParameterWeb.h>
20
21
22MixerControl::MixerControl(int32 volumeWhich)
23	:
24	fVolumeWhich(volumeWhich),
25	fGainMediaNode(media_node::null),
26	fMuteMediaNode(media_node::null),
27	fParameterWeb(NULL),
28	fMixerParameter(NULL),
29	fMuteParameter(NULL),
30	fMin(0.0f),
31	fMax(0.0f),
32	fStep(0.0f),
33	fRoster(NULL)
34{
35	fRoster = BMediaRoster::Roster();
36}
37
38
39MixerControl::~MixerControl()
40{
41	_Disconnect();
42}
43
44
45bool
46MixerControl::Connect(int32 volumeWhich, float* _value, const char** _error)
47{
48	fVolumeWhich = volumeWhich;
49
50	_Disconnect();
51
52	status_t status = B_OK;
53	const char* errorString = NULL;
54	if (fRoster == NULL)
55		fRoster = BMediaRoster::Roster(&status);
56
57	if (BMediaRoster::IsRunning() && fRoster != NULL
58			&& status == B_OK) {
59		switch (volumeWhich) {
60			case VOLUME_USE_MIXER:
61				status = fRoster->GetAudioMixer(&fGainMediaNode);
62				break;
63			case VOLUME_USE_PHYS_OUTPUT:
64				status = fRoster->GetAudioOutput(&fGainMediaNode);
65				break;
66		}
67		if (status == B_OK) {
68			status = fRoster->GetParameterWebFor(fGainMediaNode, &fParameterWeb);
69			if (status == B_OK) {
70				// Finding the Mixer slider in the audio output ParameterWeb
71				int32 numParams = fParameterWeb->CountParameters();
72				BParameter* p = NULL;
73				bool foundMixerLabel = false;
74				for (int i = 0; i < numParams; i++) {
75					p = fParameterWeb->ParameterAt(i);
76
77					// assume the mute preceeding master gain control
78					if (!strcmp(p->Kind(), B_MUTE)) {
79						fMuteParameter = p;
80						fMuteMediaNode = fMuteParameter->Web()->Node();
81					}
82
83					PRINT(("BParameter[%i]: %s\n", i, p->Name()));
84					if (volumeWhich == VOLUME_USE_MIXER) {
85						if (!strcmp(p->Kind(), B_MASTER_GAIN))
86							break;
87					} else if (volumeWhich == VOLUME_USE_PHYS_OUTPUT) {
88						/* not all cards use the same name, and
89						 * they don't seem to use Kind() == B_MASTER_GAIN
90						 */
91						if (!strcmp(p->Kind(), B_MASTER_GAIN))
92							break;
93						PRINT(("not MASTER_GAIN \n"));
94
95						/* some audio card
96						 */
97						if (!strcmp(p->Name(), "Master"))
98							break;
99						PRINT(("not 'Master' \n"));
100
101						/* some Ensonic card have all controls names 'Volume', so
102						 * need to fint the one that has the 'Mixer' text label
103						 */
104						if (foundMixerLabel && !strcmp(p->Name(), "Volume"))
105							break;
106						if (!strcmp(p->Name(), "Mixer"))
107							foundMixerLabel = true;
108						PRINT(("not 'Mixer' \n"));
109					}
110#if 0
111					//if (!strcmp(p->Name(), "Master")) {
112					if (!strcmp(p->Kind(), B_MASTER_GAIN)) {
113						for (; i < numParams; i++) {
114							p = fParamWeb->ParameterAt(i);
115							if (strcmp(p->Kind(), B_MASTER_GAIN))
116								p = NULL;
117							else
118								break;
119						}
120						break;
121					} else
122						p = NULL;
123#endif
124					p = NULL;
125				}
126				if (p == NULL) {
127					errorString = volumeWhich ? "Could not find the soundcard"
128						: "Could not find the mixer";
129				} else if (p->Type() != BParameter::B_CONTINUOUS_PARAMETER) {
130					errorString = volumeWhich ? "Soundcard control unknown"
131						: "Mixer control unknown";
132				} else {
133					fMixerParameter = static_cast<BContinuousParameter*>(p);
134					fMin = fMixerParameter->MinValue();
135					fMax = fMixerParameter->MaxValue();
136					fStep = fMixerParameter->ValueStep();
137
138					if (_value != NULL) {
139						float volume;
140						bigtime_t lastChange;
141						size_t size = sizeof(float);
142						fMixerParameter->GetValue(&volume, &size, &lastChange);
143
144						*_value = volume;
145					}
146				}
147			} else {
148				errorString = "No parameter web";
149				fParameterWeb = NULL;
150			}
151		} else
152			errorString = volumeWhich ? "No Audio output" : "No Mixer";
153
154	} else
155		errorString = "Media services not running";
156
157	if (status != B_OK) {
158		_Disconnect();
159		fMuteMediaNode = media_node::null;
160	}
161
162	if (errorString) {
163		fprintf(stderr, "MixerControl: %s.\n", errorString);
164		if (_error)
165			*_error = errorString;
166	}
167	if (fMixerParameter == NULL && _value != NULL)
168		*_value = 0;
169
170	return errorString == NULL;
171}
172
173
174bool
175MixerControl::Connected()
176{
177	return fGainMediaNode != media_node::null;
178}
179
180
181int32
182MixerControl::VolumeWhich() const
183{
184	return fVolumeWhich;
185}
186
187
188void
189MixerControl::SetMute(bool muted)
190{
191	if (fMuteParameter == NULL)
192		return;
193
194	int32 mute = muted ? 1 : 0;
195	fMuteParameter->SetValue(&mute, sizeof(int32), system_time());
196}
197
198
199bool
200MixerControl::Mute()
201{
202	if (fMuteParameter == NULL)
203		return false;
204
205	int32 mute = 0;
206	bigtime_t lastChange = 0;
207	size_t size = sizeof(int32);
208	fMuteParameter->GetValue(&mute, &size, &lastChange);
209	return mute != 0;
210}
211
212
213float
214MixerControl::Volume() const
215{
216	if (fMixerParameter == NULL)
217		return 0.0f;
218
219	float volume = 0;
220	bigtime_t lastChange;
221	size_t size = sizeof(float);
222	fMixerParameter->GetValue(&volume, &size, &lastChange);
223
224	return volume;
225}
226
227
228void
229MixerControl::SetVolume(float volume)
230{
231	if (fMixerParameter == NULL)
232		return;
233
234	if (volume < fMin)
235		volume = fMin;
236	else if (volume > fMax)
237		volume = fMax;
238
239	if (volume != Volume())
240		fMixerParameter->SetValue(&volume, sizeof(float), system_time());
241}
242
243
244void
245MixerControl::ChangeVolumeBy(float value)
246{
247	if (fMixerParameter == NULL || value == 0.0f)
248		return;
249
250	float volume = Volume();
251	SetVolume(volume + value);
252}
253
254
255void
256MixerControl::_Disconnect()
257{
258	delete fParameterWeb;
259	fParameterWeb = NULL;
260	fMixerParameter = NULL;
261
262	if (fRoster == NULL)
263		fRoster = BMediaRoster::Roster();
264
265	if (fRoster != NULL && fGainMediaNode != media_node::null)
266		fRoster->ReleaseNode(fGainMediaNode);
267
268	fGainMediaNode = media_node::null;
269}
270