1/*
2 * Copyright 2003-2009, 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 */
10
11
12#include "MixerControl.h"
13
14#include <string.h>
15
16#include <Debug.h>
17#include <ParameterWeb.h>
18
19
20MixerControl::MixerControl(int32 volumeWhich)
21	:
22	fVolumeWhich(volumeWhich),
23	fGainMediaNode(media_node::null),
24	fParameterWeb(NULL),
25	fMixerParameter(NULL),
26	fMin(0.0f),
27	fMax(0.0f),
28	fStep(0.0f)
29{
30}
31
32
33MixerControl::~MixerControl()
34{
35	_Disconnect();
36}
37
38
39bool
40MixerControl::Connect(int32 volumeWhich, float* _value, const char** _error)
41{
42	fVolumeWhich = volumeWhich;
43
44	_Disconnect();
45
46	bool retrying = false;
47
48	status_t status = B_OK;
49		// BMediaRoster::Roster() doesn't set it if all is ok
50	const char* errorString = NULL;
51	BMediaRoster* roster = BMediaRoster::Roster(&status);
52
53retry:
54	// Here we release the BMediaRoster once if we can't access the system
55	// mixer, to make sure it really isn't there, and it's not BMediaRoster
56	// that is messed up.
57	if (retrying) {
58		errorString = NULL;
59		PRINT(("retrying to get a Media Roster\n"));
60		/* BMediaRoster looks doomed */
61		roster = BMediaRoster::CurrentRoster();
62		if (roster) {
63			roster->Lock();
64			roster->Quit();
65		}
66		snooze(10000);
67		roster = BMediaRoster::Roster(&status);
68	}
69
70	if (roster != NULL && status == B_OK) {
71		switch (volumeWhich) {
72			case VOLUME_USE_MIXER:
73				status = roster->GetAudioMixer(&fGainMediaNode);
74				break;
75			case VOLUME_USE_PHYS_OUTPUT:
76				status = roster->GetAudioOutput(&fGainMediaNode);
77				break;
78		}
79		if (status == B_OK) {
80			status = roster->GetParameterWebFor(fGainMediaNode, &fParameterWeb);
81			if (status == B_OK) {
82				// Finding the Mixer slider in the audio output ParameterWeb
83				int32 numParams = fParameterWeb->CountParameters();
84				BParameter* p = NULL;
85				bool foundMixerLabel = false;
86				for (int i = 0; i < numParams; i++) {
87					p = fParameterWeb->ParameterAt(i);
88					PRINT(("BParameter[%i]: %s\n", i, p->Name()));
89					if (volumeWhich == VOLUME_USE_MIXER) {
90						if (!strcmp(p->Kind(), B_MASTER_GAIN))
91							break;
92					} else if (volumeWhich == VOLUME_USE_PHYS_OUTPUT) {
93						/* not all cards use the same name, and
94						 * they don't seem to use Kind() == B_MASTER_GAIN
95						 */
96						if (!strcmp(p->Kind(), B_MASTER_GAIN))
97							break;
98						PRINT(("not MASTER_GAIN \n"));
99
100						/* some audio card
101						 */
102						if (!strcmp(p->Name(), "Master"))
103							break;
104						PRINT(("not 'Master' \n"));
105
106						/* some Ensonic card have all controls names 'Volume', so
107						 * need to fint the one that has the 'Mixer' text label
108						 */
109						if (foundMixerLabel && !strcmp(p->Name(), "Volume"))
110							break;
111						if (!strcmp(p->Name(), "Mixer"))
112							foundMixerLabel = true;
113						PRINT(("not 'Mixer' \n"));
114					}
115#if 0
116					//if (!strcmp(p->Name(), "Master")) {
117					if (!strcmp(p->Kind(), B_MASTER_GAIN)) {
118						for (; i < numParams; i++) {
119							p = fParamWeb->ParameterAt(i);
120							if (strcmp(p->Kind(), B_MASTER_GAIN))
121								p = NULL;
122							else
123								break;
124						}
125						break;
126					} else
127						p = NULL;
128#endif
129					p = NULL;
130				}
131				if (p == NULL) {
132					errorString = volumeWhich ? "Could not find the soundcard"
133						: "Could not find the mixer";
134				} else if (p->Type() != BParameter::B_CONTINUOUS_PARAMETER) {
135					errorString = volumeWhich ? "Soundcard control unknown"
136						: "Mixer control unknown";
137				} else {
138					fMixerParameter = static_cast<BContinuousParameter*>(p);
139					fMin = fMixerParameter->MinValue();
140					fMax = fMixerParameter->MaxValue();
141					fStep = fMixerParameter->ValueStep();
142
143					if (_value != NULL) {
144						float volume;
145						bigtime_t lastChange;
146						size_t size = sizeof(float);
147						fMixerParameter->GetValue(&volume, &size, &lastChange);
148
149						*_value = volume;
150					}
151				}
152			} else {
153				errorString = "No parameter web";
154			}
155		} else {
156			if (!retrying) {
157				retrying = true;
158				goto retry;
159			}
160			errorString = volumeWhich ? "No Audio output" : "No Mixer";
161		}
162	} else {
163		if (!retrying) {
164			retrying = true;
165			goto retry;
166		}
167		errorString = "No Media Roster";
168	}
169
170	if (status != B_OK)
171		fGainMediaNode = media_node::null;
172
173	if (errorString) {
174		fprintf(stderr, "MixerControl: %s.\n", errorString);
175		if (_error)
176			*_error = errorString;
177	}
178	if (fMixerParameter == NULL && _value != NULL)
179		*_value = 0;
180
181	return errorString == NULL;
182}
183
184
185bool
186MixerControl::Connected()
187{
188	return fGainMediaNode != media_node::null;
189}
190
191
192int32
193MixerControl::VolumeWhich() const
194{
195	return fVolumeWhich;
196}
197
198
199float
200MixerControl::Volume() const
201{
202	if (fMixerParameter == NULL)
203		return 0.0f;
204
205	float volume = 0;
206	bigtime_t lastChange;
207	size_t size = sizeof(float);
208	fMixerParameter->GetValue(&volume, &size, &lastChange);
209
210	return volume;
211}
212
213
214void
215MixerControl::SetVolume(float volume)
216{
217	if (fMixerParameter == NULL)
218		return;
219
220	if (volume < fMin)
221		volume = fMin;
222	else if (volume > fMax)
223		volume = fMax;
224
225	if (volume != Volume())
226		fMixerParameter->SetValue(&volume, sizeof(float), system_time());
227}
228
229
230void
231MixerControl::ChangeVolumeBy(float value)
232{
233	if (fMixerParameter == NULL || value == 0.0f)
234		return;
235
236	float volume = Volume();
237	SetVolume(volume + value);
238}
239
240
241void
242MixerControl::_Disconnect()
243{
244	delete fParameterWeb;
245	fParameterWeb = NULL;
246	fMixerParameter = NULL;
247
248	BMediaRoster* roster = BMediaRoster::CurrentRoster();
249	if (roster != NULL && fGainMediaNode != media_node::null)
250		roster->ReleaseNode(fGainMediaNode);
251
252	fGainMediaNode = media_node::null;
253}
254