Entwickler-Ecke

Windows API - Lautstärkemixer regeln


IhopeonlyReader - Do 02.01.14 13:31
Titel: Lautstärkemixer regeln
Guten Tag,
wie der Titel schon sagt, möchte ich von meinem Programm aus, die Lautstärke einzelner Anwendungen/Prozesse regeln.
Hierzu habe ich viel gefunden, meist wurde die Komponente TAudioVolume empfohlen. Diese habe ich ausprobiert, allerdings schaffe ich es nur die Gesamtlautstärke (also die vom Lautsprecher) oder die der eigenen Anwendung zu verändern.

Ich würde gerne:
Alle Programmnamen/Prozessnamen listen, die auch im Windows-Lautsätrkemixer vorhanden sind
und diese dann von der Lautstärke verändern können


Sinn ?!: ich will wenn ich z.B. ein Video schaue Systemsounds ausschalten oder so, ohne das was ich offen habe zu minimieren...
Shortcuts legen etc. ist alles kein Problem, aber die 2 Punkte oben kriege ich nicht hin :( und die Gesamtlautstärke runterregeln wäre sinnlos :D

mfG IHopeOnlyReader


Delete - Fr 03.01.14 10:20

http://www.torry.net/vcl/mmedia/audio/TAudioVolumev093Eng.zip


IhopeonlyReader - Fr 03.01.14 14:19

wie ich schon sagte, TAUdioVolumen hatte ich bereits ausprobiert..
in der Demo kann ich folgendes auswählen:
mode: Windows Vista, Windows 7
Device kann ich auswählen:
- Lautsprecher (System)
- Realtek Digital Output
- Line 1 (Vitrual Audio Cable)


Die Outputs werden mir entsprechend angezeigt...
unten kann ich leider nur das Mstervolumen und das der eignen Application ändern..

es ist also nichts dabei von dem was ich brauch/ haben will...
ich möchte die Lautstärke ANDERER Programme/Prozesse regeln..

sorry, aber genau dein Link hatte ich im Threadstart schon beschrieben, dass das falsche ist


jaenicke - Fr 03.01.14 21:49

Du solltest dir dafür einmal das Interface IAudioSessionManager anschauen:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370948(v=vs.85).aspx
Damit bekommst du Zugriff auf die aktiven Audio Sessions der einzelnen Prozesse. Du kannst dann über das ISimpleAudioVolume Interface diese Session muten bzw. entmuten :D und die Lautstärke steuern:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd316531(v=vs.85).aspx


IhopeonlyReader - Mo 06.01.14 17:59

kannst du bitte genauer Erläutern wie? ich kam mit diesen microsoft-seiten noch nie so klar..

Ziel: Unit mit 3 funktionen + 1 prozedure aufbauen:
1. function SoundProzessesCount: Integer;
2. function NameOfSoundProzess( aID: Integer ): String;
3. function GetSoundLevel( aID: Integer ): Byte;
4. procedure setSoundLevel( aID: Integer; SoundLevel: Byte );


nur wie kriege ich diese proceduren umgesetzt?
wie gesagt, ich durchblicke die "Microsoft"-Seiten nicht...


IhopeonlyReader - Di 07.01.14 21:47

-push-


IhopeonlyReader - Do 09.01.14 15:11

-push-


baumina - Do 09.01.14 15:28

*gelöscht*


jaenicke - Do 09.01.14 19:59

Eigentlich hatte ich nicht die Absicht zu antworten, wenn du offenbar gar nicht erst selbst anfängst, aber ich poste trotzdem mal erst einmal was ich in Google eben in 10 Sekunden bei der ersten Suche gefunden habe:
http://stackoverflow.com/questions/12660480/exception-calling-iaudiosessionmanager2-getsessionenumerator-in-delphi-xe2
Sollte die Definition nicht funktionieren und du keine andere finden, bleibt nur das selbst aus der Headerdatei abzuschreiben. Welche das ist steht ja in der Doku.


gerd8888 - Do 09.01.14 22:00

Ich hatte auch das Problem, dass ich nur die Lautstärke von dem Video und nicht die gesamte Lautstärke (von Windows) einstellen wollte.
Hier kurze Anmerkungen. Weiss nicht, ob das weiterhilft?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
const
  MCI_SETAUDIO = $0873;
  MCI_DGV_SETAUDIO_VOLUME = $4002;
  MCI_DGV_SETAUDIO_ITEM = $00800000;
  MCI_DGV_SETAUDIO_VALUE = $01000000;
  MCI_DGV_STATUS_VOLUME = $4019;

type
  MCI_DGV_SETAUDIO_PARMS = record
    dwCallback: DWORD;
    dwItem: DWORD;
    dwValue: DWORD;
    dwOver: DWORD;
    lpstrAlgorithm: PChar;
    lpstrQuality: PChar;
  end;

type
  MCI_STATUS_PARMS = record
    dwCallback: DWORD;
    dwReturn: DWORD;
    dwItem: DWORD;
    dwTrack: DWORD;
  end;

procedure SetVolume(const volL, volR: Word);
var
  hWO: HWAVEOUT;
  waveF: TWAVEFORMATEX;
  vol: DWORD;
begin
  // init TWAVEFORMATEX
  FillChar(waveF, SizeOf(waveF), 0);
  // open WaveMapper = std output of playsound
  waveOutOpen(@hWO, WAVE_MAPPER, @waveF, 000);
  vol:= volL + volR shl 16;
  // set volume
  waveOutSetVolume(hWO, vol);
  waveOutClose(hWO);
end;

procedure SetMPVolume(MP: TMediaPlayer; Volume: Integer);
  { Volume: 0 - 1000 }
var
  p: MCI_DGV_SETAUDIO_PARMS;
begin
  { Volume: 0 - 1000 }
  p.dwCallback := 0;
  p.dwItem := MCI_DGV_SETAUDIO_VOLUME;
  p.dwValue := Volume;
  p.dwOver := 0;
  p.lpstrAlgorithm := nil;
  p.lpstrQuality := nil;
  mciSendCommand(MP.DeviceID, MCI_SETAUDIO,
    MCI_DGV_SETAUDIO_VALUE or MCI_DGV_SETAUDIO_ITEM, Cardinal(@p));
end;


jaenicke - Fr 10.01.14 00:21

Das ist ja nicht prozessspezifisch. Das ist ja genau die alte gerätespezifische Herangehensweise, die heute ja nicht mehr wirklich Sinn macht.


gerd8888 - Fr 10.01.14 00:46

Hallo jaenicke,

ich weiss nicht, was Du mit prozessspefisch meinst. Jedenfalls konnte ich damit Originalvideo und englische Uebersetzung (also 2 verschiedene Videos) miteinander mixen, wenn ich es wollen hätte. Man kann das auch theoretisch mit mehr als 2 Videos machen. Für meine Zwecke habe ich das Originalvideo ganz leise gestellt und das andere dann ganz laut. Weil beide Sprachen gleichzeitig zu hören keinen Sinn machen. Aber wie gesagt theoretisch waere das moeglich.
Ich war damit recht zufrieden.

(wobei das zweite Video eine .wav Datei war, also kein eigenes Video)

Gerd


jaenicke - Fr 10.01.14 06:31

Was mit prozessspezifisch gemeint ist, hat er doch am Anfang auch geschrieben:
user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
Ich würde gerne:
Alle Programmnamen/Prozessnamen listen, die auch im Windows-Lautsätrkemixer vorhanden sind
und diese dann von der Lautstärke verändern können
Es geht nicht um wave-Geräte oder irgendetwas und schon gar nicht um den eigenen Prozess, sondern es geht darum, dass im Lautstärkeregler von Windows ja z.B. der Browser als regelbar drin steht, wenn im Browser gerade z.B. Musik läuft, und standardmäßig z.B. Systemsounds.
Und genauso wie man diese Lautstärken dort regeln kann möchte er das halt im eigenen Programm machen.

Einen anderen Weg als über die genannten Interfaces gibt es meines Wissens nicht.


gerd8888 - Fr 10.01.14 13:30

verstehe, das duerte etwas schwieriger sein. Ich stelle mir das gerade mit den Browser vor. Es gibt ja mehrere Browser und so weiter... Da waere es vielleicht gut, wenn man direkt an den Mixer von Windows dran kommt? Da kenne ich mich nicht aus. Viel Spass...


jaenicke - Fr 10.01.14 16:00

user profile icongerd8888 hat folgendes geschrieben Zum zitierten Posting springen:
Da waere es vielleicht gut, wenn man direkt an den Mixer von Windows dran kommt?
Genau um an diese Sessions zu kommen dient doch das genannte Interface.


IhopeonlyReader - Fr 10.01.14 20:16

so, ich habe nun ein Projekt compiliert bekommen, in der ich WinVis78Mixer verwende, da ich Windows 7 habe, müsste es damit funktionieren.
Leider gibt

Delphi-Quelltext
1:
function GetChannelVolume(Channel: DWord; Value: integer): integer; override;                    

dauerhaft einen Fehler aus.


Die Function

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
function TVis78Mixer.GetDevice(DeviceId: integer): string;
var
  _PropertyStore: IPropertyStore;
  _var: TPropVariant;

begin
  Result:= '';
  HRES:= DefaultDevice.OpenPropertyStore(STGM_READ, _PropertyStore);
  if SUCCEEDED(HRES) then
    begin
      // Get the DirectSound or DirectSoundCapture device GUID
      // (in WCHAR string format) for the endpoint device.
      HRES:= _PropertyStore.GetValue(PKEY_Device_FriendlyName, _var);
      if Failed(HRES) then
        Raise Exception.Create('GetDevice failed')
      else
        Result:= '';
    end;
end;

liefert natürlich immer '' (also nichts) als ergebnis (keinen fehler!)... warum hat sie einen Rückgabewert?



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
FBM.MasterVolume := Round( 53 *(65535/100) ); //setzt das Systemvolumen auf 53 %, klappt super (FBM ist mein TBasicMixer, der mit TWinVis78Mixer.create erstellt wurde)
FBM.SetChannelVolume(   1 ,    Round(49 *(65535/100))); //klappt nur teilweise !
// Teilweise ist der Channel 0 "Lautsprecher" teilweise aber auch Channel 1.. entweder ist es das was im quelltext steht, oder es wird eine Exception ausgeworfen.
// Es ist wirklich mal das, mal das.. 10x klappt es mit 0 , dann plötzlich fehlermeldung,, dann klappt es 5x mit channel 1 wieder fehlermeldung und "lautsprecher" liegen //wieder auf channel 0....

// Andere Channel außer dem "Lautsprecher" existieren scheinbar nicht. Abgesehen von Channel 0 bzw. 1 (GEsamtlautstärke) funktioniert kein anderer Channel....
//Soweit war ich auch mit anderen Quelltexten schon...

Danke dass du dich bemühst, aber woran könnte es liegen?
(Ich benutze den von dir geposteten Link die Dateien... in der Unit Win78Mixer sind die Mixer definiert. im Projekt rufe ich es so auf:

Delphi-Quelltext
1:
2:
3:
4:
5:
//FBM := TVis78Mixer.create;
FBM := CreateVis78Mixer;
//FBM.MasterVolume := Round( 53 *(65535/100) );
FBM.SetChannelVolume(   1 ,    Round(49 *(65535/100)));
FBM.Free;


jaenicke - Sa 11.01.14 01:28

Der Mixer geht ja auch nur die Endpunkte durch und nicht die Sessions, der wird dir alleine nicht viel helfen. Grad mal ausprobiert, das funktioniert eigentlich genau wie in der Doku steht (mit den Units, die du von dem Link hast):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
uses
  MMDeviceApi, ActiveX, AudioPolicy;

var
  DeviceEnumerator: IMMDeviceEnumerator;
  DefaultDevice: IMMDevice;
  SessionManager: IAudioSessionManager;
  SessionControl: IAudioSessionControl;
begin
  if Succeeded(CoCreateInstance(CLSID_MMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, DeviceEnumerator))
    and Succeeded(DeviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, DefaultDevice))
    and Succeeded(DefaultDevice.Activate(IID_IAudioSessionManager, CLSCTX_INPROC_SERVER, nil, IUnknown(SessionManager)))
    and Succeeded(SessionManager.GetAudioSessionControl(nil0, SessionControl)) then
    SessionControl.SetDisplayName('Mein Programm'nil);
Danach siehst du das eigene Projekt mit der Beschriftung "Mein Programm" im Windows Mixer.

Analog kannst du die anderen Funktionen der Interfaces ja auch nutzen.

// EDIT:
Fehlende Klammer ergänzt


Delete - Sa 11.01.14 12:29

Eine andere Lösung:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
uses 
  ShellAPI;

procedure TForm1.ButtonClick(Sender: TObject);
begin
  if Win32MajorVersion > 5 then
      ShellExecute(0,nil,PChar('sndvol.exe'),'-d0 -r','',SW_SHOWNORMAL )
   else
      ShellExecute(0,nil,PChar('sndvol32.exe'),'','',SW_SHOWNORMAL );
end;


IhopeonlyReader - So 12.01.14 18:46

Guten Tag,

Delphi-Quelltext
1:
2:
3:
4:
5:
if Succeeded(CoCreateInstance(CLSID_MMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, DeviceEnumerator))
    and Succeeded(DeviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, DefaultDevice))
    and Succeeded(DefaultDevice.Activate(IID_IAudioSessionManager, CLSCTX_INPROC_SERVER, nil, IUnknown(SessionManager))
    and Succeeded(SessionManager.GetAudioSessionControl(nil0, SessionControl)) then
    SessionControl.SetDisplayName('Mein Programm'nil);

liefert folgenden fehler:

Delphi-Quelltext
1:
[Fehler] Unit1.pas(35): Operator ist auf diesen Operandentyp nicht anwendbar                    

Zeile 35 ist diese "and Succeeded(SessionManager.GetAudioSessionControl(nil, 0, SessionControl)) then"

somit funktioniert nicht einmal das beispiel :(


jaenicke - So 12.01.14 20:52

Ja, da war eine Klammer verloren gegangen, hab sie mal ergänzt, siehe oben...