INTERACT FORUM
Windows => Plug-in Development => Topic started by: pbou31 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.
-
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.
-
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
-
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.
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.
-
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 (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 (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
-
Hello
I'm back with my working solution. Here is the script:
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 (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 (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
-
Nice job!! It's really cool to see that you took this all the way to a completed project. Congratulations!
Brian.
-
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?
-
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