INTERACT FORUM

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1]   Go Down

Author Topic: Adjust System Volume automatically from specific field  (Read 19952 times)

pbou31

  • Recent member
  • *
  • Posts: 5
Adjust System Volume automatically from specific field
« on: April 10, 2016, 06:02:56 am »

Hi

I’m using JRiver (MC 19) for audiophile music listening with my hi-fi system. My library is organized mainly by folders corresponding to audio quality (flac/CD quality, HD, DSD, MP3) then by main genres (Jazz, classical, pop, rock, variety, blues, world music), then by artist/composer, and then by albums. I know this may be strange and a little “old-fashioned”. So when I am listening to music, I want to know in which part I am (HD or CD or MP3 for example). For this, I am using a folder view (Audio -> Files in the left panel) in Standard view. Then, I navigate from folders to folders and select the genre/artist/track I want to listen. I don’t use any playlist in general.

For each track, I have a preferred system volume level (percentage corresponding to volume slider position) that I record in one of the fields (BIOS field). So my standard view always display in the left pane the Tag part, allowing me to visualise / edit this field. When I want to listen to one track, I click on this track, look at the BIOS information (for instance "78%"), set the system volume to 78% using my remote control (vol up or vol down), then click on Play. I shall add that I have activated “Volume levelling” and when I import audio files, I always “Analyze Audio”. So when I play tracks, a volume correction is applied. So my ideal system volume is the system volume level when this correction is done.

I am wondering if it would be possible to develop some code (JRiver plug-in for instance?) to automatically set the system volume to the value read in associated BIOS field before playback of each track?

If yes, I will do my best trying to do it (if you have any main steps to show me the way, it will be welcome: such as what language, what SDK, what functions…). I never developed in MS Windows environment (only VBA macros for MS Excel), but I used to be a developer (when I was younger…with languages such as Pascal, Ada, C, Assembly (yes, it was long time ago)).

Thanks by advance for your help.
Logged

blgentry

  • Regular Member
  • Citizen of the Universe
  • *****
  • Posts: 8014
Re: Adjust System Volume automatically from specific field
« Reply #1 on: April 10, 2016, 11:19:56 am »

At first I was thinking that the DSP per track might be a good solution for you.  That way you could set a boost or cut in the parametric EQ for each track.  But this would become really cumbersome, as you have to save each one as a preset, and you might end up with a lot of presets depending on how much boost or cut you want and how many combinations there are.

Another way to do this would be with some kind of integration with MCWS.  MCWS can send absolute volume commands.  So you can set a zone to 78% directly through an MCWS call.  The question then becomes, how do you get some program to read the appropriate tag in a song and then submit an MCWS command?  You would almost have to write a program kind of like JRemote that could figure out what song was about to play and right at playback time, issue the volume command via MCWS.

I'm not sure what to advise you on this; just some thoughts.

Brian.
Logged

pbou31

  • Recent member
  • *
  • Posts: 5
Re: Adjust System Volume automatically from specific field
« Reply #2 on: April 12, 2016, 02:13:46 am »

Thanks Brian for your short delay answer.

I am not convinced by your first idea, but the second one seems a good start. I have questions about it:
1.   The customized “remote control reception program” will it be a dll? Inserted in JRiver as a Plug-in?
2.   How is it possible to bypass the standard remote control reception program, meaning it will not process the remote control commands, but my program will?
3.   The program will have to know on what track the selection is => how?
4.   It will have to read the BIOS field => with what function?
5.   It will have to set the system volume => with what function?
6.   It will have to launch the track playback => with what function?
7.   Then, the problem I see is when the track playback will finish, normally, the following one (on the same album) will be played, but the system volume should be adjusted first, if needed. For this part, I don’t see how it will be possible. Or it means that my “remote control reception program” will keep control of complete playback replacing the standard JRiver one. Is it correct? In other words, what will guarantee that at the end of one track playback, the standard JRiver playback engine will not get the hand (to launch next track playback) but my program will?

Thanks by advance if you can help replying some of these questions.

Philippe
Logged

blgentry

  • Regular Member
  • Citizen of the Universe
  • *****
  • Posts: 8014
Re: Adjust System Volume automatically from specific field
« Reply #3 on: April 12, 2016, 08:13:11 am »

1.   The customized “remote control reception program” will it be a dll? Inserted in JRiver as a Plug-in?

Good question.  I know that plugins can be written for MC on Windows.  I've never researched this capability.  Maybe you should read up on the various ways you can do development with MC:

https://wiki.jriver.com/index.php/DevZone

My original suggestion of using MCWS was simply because I know some MCWS commands; it's not necessarily the ideal solution.

Quote
3.   The program will have to know on what track the selection is => how?
4.   It will have to read the BIOS field => with what function?
5.   It will have to set the system volume => with what function?

MCWS is self documenting.  Open this URL on the same computer where you run MC and you'll see a nice list of commands:

http://localhost:52199/MCWS/v1/

As I said, MCWS might not be the right solution.  A plugin would seem to be more "hooked in" to the MC playback process and might have more immediate access to all of this.  I'm sorry I can't give you any advice on that, as I haven't researched it at all.

Good luck!

Brian.
Logged

pbou31

  • Recent member
  • *
  • Posts: 5
Re: Adjust System Volume automatically from specific field
« Reply #4 on: April 13, 2016, 11:23:49 am »

Ok, thanks for the trick. You are right, once JRiver running, opening this URL from JRiver opens a web page in the browser with this documentation.

From my side, I also found these commands here https://wiki.jriver.com/index.php/Media_Center_Automation:
  • To catch a PLAY event (depending on language): in the Media Center Automation page, Event Handling part
  • To read a field: (MJFileAutomation) string Get(string strField, boolean bFormatted)
  • To set volume: (MJMixerAutomation) number Volume() (read / write)
  • To start playback: (MJPlaybackAutomation) void Play()
  • To jump to next track: (MJPlaybackAutomation) void Next()
  • To get information on zone: (MJZoneAutomation)

And information to start with Visual Studio: http://wiki.jriver.com/index.php/How_to_use_MC_methods_in_.Net_projects

I will try on my own with all this information

Thanks again Brian for your help.

Philippe
Logged

pbou31

  • Recent member
  • *
  • Posts: 5
Re: Adjust System Volume automatically from specific field
« Reply #5 on: May 09, 2016, 05:29:41 am »

Hello
I'm back with my working solution. Here is the script:

Quote
Imports System
Imports System.Windows.Forms
Imports Microsoft.VisualBasic
Imports System.Threading

' //css_reference System.dll;
' //css_reference System.Windows.Forms.dll;
' //css_reference MediaCenter.dll;

Class Script
    Inherits MarshalByRefObject

    Dim WithEvents MC As MediaCenter.MCAutomation
    'Function that gets the current track played, read "Bios" field and extract target volume
    Function GetVolumeForCurrentTrack(MCInstance As MediaCenter.MCAutomation) As Integer
        'System.Console.WriteLine("GVFCT")
        'Get and save system volume. This will be the value returned in case no file is being played
        Dim MCMixer As MediaCenter.IMJMixerAutomation
        MCMixer = MCInstance.GetMJMixer()
        GetVolumeForCurrentTrack = MCMixer.Volume

        'Get Zones
        Dim MJZones As MediaCenter.IMJZonesAutomation
        MJZones = MCInstance.GetZones()

        'Get active zone name
        Dim MJActZoneID As Integer
        Dim MJActZone As MediaCenter.IMJZoneAutomation
        MJActZoneID = MJZones.GetActiveZone()
        MJActZone = MJZones.GetZone(MJActZoneID)
        'System.Console.WriteLine("GVFCT: " & MJActZone.GetName())

        'Get Playback
        Dim MJPlayback As MediaCenter.IMJPlaybackAutomation
        Dim MJPBState As MediaCenter.MJPlaybackStates
        Dim DisplayablePlayback As String
        MJPlayback = MJActZone.GetPlayback()
        MJPBState = MJPlayback.State

        'Display playback state
        Select Case MJPBState
            Case MediaCenter.MJPlaybackStates.PLAYSTATE_STOPPED
                DisplayablePlayback = "PLAYSTATE_STOPPED"
            Case MediaCenter.MJPlaybackStates.PLAYSTATE_PAUSED
                DisplayablePlayback = "PLAYSTATE_PAUSED"
            Case MediaCenter.MJPlaybackStates.PLAYSTATE_PLAYING
                DisplayablePlayback = "PLAYSTATE_PLAYING"
            Case Else 'MediaCenter.MJPlaybackStates.PLAYSTATE_WAITING
                DisplayablePlayback = "PLAYSTATE_WAITING"
        End Select
        'System.Console.WriteLine("GVFCT: " & DisplayablePlayback)

        'If a file is being played
        If MJPBState <> MediaCenter.MJPlaybackStates.PLAYSTATE_STOPPED Then
            'Get currently played file
            Dim MJFile As MediaCenter.IMJFileAutomation
            MJFile = MJActZone.GetPlayingFile()

            'Display the name of the current track played
            'System.Console.WriteLine("GVFCT: " & MJFile.Name)

            'Read "Bios" field
            Dim MJBios As String
            MJBios = MJFile.Get("Bios", False)
            'System.Console.WriteLine("GVFCT: " & MJBios)

            'Look if "Bios" field contains "%" character
         'My Bios field is filled with volume as a percentage, so for instance: "82%"
         'It also happens that an old volume information is stored, without "%". In this case, the procedure ignores it.
         'Operational values for me are roughly in the intervalle 60%-90%
            Dim PercentPos As Integer
            Dim PercentChar As String
            PercentChar = "%"
            PercentPos = MJBios.IndexOf(PercentChar)
            'System.Console.WriteLine("GVFCT: " & CStr(PercentPos))

            'If "Bios" field contains "%" (at the right place), then get the volume value
            If PercentPos = 2 Then
                'Try to convert it into integer => delete "%" then convert
                MJBios = Left(MJBios, 2)
                'System.Console.WriteLine("GVFCT: " & MJBios)

                GetVolumeForCurrentTrack = CInt(MJBios)
                'System.Console.WriteLine("GVFCT: " & MJFile.Name & " -> " & CStr(GetVolumeForCurrentTrack))
                'If "Bios" does not contain correct information, then set volume to default value
            Else
                GetVolumeForCurrentTrack = 70
            End If
        End If
    End Function

    Sub SetSystemVolume(MCInstance As MediaCenter.MCAutomation, TargetVolume As Integer)
        'Write Init Message
        'System.Console.WriteLine("SSV: Sets System Volume to" & CStr(TargetVolume))

        'Sets the system volume to this value
        Dim MCMixer As MediaCenter.IMJMixerAutomation
        MCMixer = MCInstance.GetMJMixer()
        'Avoid to generate volume change event when volume stays the same
        If TargetVolume <> MCMixer.Volume Then
            MCMixer.Volume = TargetVolume
        End If
    End Sub

    Sub Init(unusedRef As MediaCenter.MCAutomation)
        Dim mediaCenterInterface As MediaCenter.MCAutomation
        mediaCenterInterface = System.Runtime.InteropServices.Marshal.GetActiveObject("MediaJukebox Application")
        'Init procedure will adjust volume for the first time (at script initialisation).
        'mediaCenter_FireMJEventHandler will then do it each time a new track is played
        SetSystemVolume(mediaCenterInterface, GetVolumeForCurrentTrack(mediaCenterInterface))

        MC = mediaCenterInterface
       
        'System.Console.WriteLine("Init: Waiting on Events")
        AddHandler mediaCenterInterface.FireMJEvent, AddressOf mediaCenter_FireMJEventHandler
        System.Threading.Thread.Sleep(30000000)

        'System.Console.WriteLine("Stopped Waiting on Events")
    End Sub

    Public Sub mediaCenter_FireMJEventHandler(ByVal bstrType As String, ByVal bstrParam1 As String, ByVal bstrParam2 As String) Handles MC.FireMJEvent
        'System.Console.WriteLine("FMJEH: " & bstrParam1)

        If bstrParam1 = "MCC: NOTIFY_TRACK_CHANGE" Then
            'System.Console.WriteLine("FMJEH: Track change")
         SetSystemVolume(MC, GetVolumeForCurrentTrack(MC))
        End If
    End Sub

End Class

This version is working fine. It may be added with the Script plugin. To do that:

- Download  Script Plugin (http://yabb.jriver.com/interact/index.php?topic=38693.0) (version 0.6.5.0)
- To integrate in Media Center: double click on 833.mjp. It downloads 833.zip
- Install plugin in folder: C:\Program Files (x86)\J River\Media Center 19\Plugins\Script Plugin
- Modify folder access rights to add your user with total control for this folder: C:\Program Files (x86)\J River\Media Center 19\Plugins\Script Plugin\Scripts
- Launch JRiver
- In Left panel, click on Services & Plug-ins -> Scripting Plugin -> New Script. Paste the code (I add difficulties with some copy/paste regarding TAB characters. From Visual Studio editor, it works fine.
- Run Script

Some advice if you try your own script: as the error message won't give you much information to know where the error is, the best method seems to be to paste line after line (or couple!). Using Visual Studio to first ensure no error remains will also increase your chances.

Then, when it works fine, you can use Script Runner (http://yabb.jriver.com/interact/index.php?topic=51552.0) to launch it as a script. As it is an old post, you may have errors, setting up the corresponding environment. I finally succeeded, I think when replacing some of downloaded files with my current ones.

This version of script is not beautiful as it uses a sleep for a specific duration. It seems it is possible to use Delegate and BeginInvoke. For me, I have always had an error calling the BeginInvoke.

That's it. I hope it will help someone...

Philippe
Logged

blgentry

  • Regular Member
  • Citizen of the Universe
  • *****
  • Posts: 8014
Re: Adjust System Volume automatically from specific field
« Reply #6 on: May 09, 2016, 07:48:33 am »

Nice job!!  It's really cool to see that you took this all the way to a completed project.  Congratulations!

Brian.
Logged

Johnmarkp

  • Member
  • *
  • Posts: 1
Re: Adjust System Volume automatically from specific field
« Reply #7 on: May 31, 2016, 06:20:24 pm »

Nice work indeed.  I was starting a project to make this work on my system.  I assume that I can use the R128 Volume Level for this using this.  Dim MJFile As MediaCenter.IMJFileAutomation
            MJFile = MJActZone.GetPlayingFile(). 

I am just starting the project and need to get into the script language.  Your work should save me a lot of time and effort.

Why didn't you use the R128 number?
Logged

pbou31

  • Recent member
  • *
  • Posts: 5
Re: Adjust System Volume automatically from specific field
« Reply #8 on: June 11, 2016, 09:57:23 am »

Depending of what I am listening, I like to decide what is the volume level that gives me the most pleasure. So R128 is not what I expect.
BR
Philippe
Logged
Pages: [1]   Go Up