logo

Live Production Software Forums


Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

2 Pages12>
Options
Go to last post Go to first unread
jhebbel  
#1 Posted : Thursday, October 29, 2015 7:40:08 PM(UTC)
jhebbel

Rank: Advanced Member

Groups: Registered
Joined: 10/28/2015(UTC)
Posts: 183

Thanks: 7 times
Was thanked: 15 time(s) in 14 post(s)
I have no doubt that vMix will at some point support MIDI out to allow us to use our indicators on our MIDI devices as a tally board, but in the meantime this workaround is tested and works wonderfully. The code will work out of the boc for the APC40, other devices will need to modify their channels and notes. This will dynamically light up clips on your board as shots are added to vMix, it will also re-order lights if tracks are re-ordered in vMix. Active overlays will also show as live.

Uses the midi-dot-net project from https://code.google.com/p/midi-dot-net/ to simplify the MIDI out process.

Code:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using Midi;
using System.Threading;
using System.Net;
using System.Text.RegularExpressions;

namespace APC40_Shot_Selector
{
    public partial class Form1 : Form
    {
        List<shotKey> shots;
        Thread shotListThread, shotStatusThread;
        public Form1()
        {
            InitializeComponent();
            ExtensionMethods.outputDevice = OutputDevice.InstalledDevices.FirstOrDefault(d => d.Name == "Akai APC40");
            ExtensionMethods.outputDevice.Open();

            shots = new List<shotKey>();

            shots.Add(new shotKey(1, Channel.Channel1, Pitch.F3));
            shots.Add(new shotKey(2, Channel.Channel2, Pitch.F3));
            shots.Add(new shotKey(3, Channel.Channel3, Pitch.F3));
            shots.Add(new shotKey(4, Channel.Channel4, Pitch.F3));
            shots.Add(new shotKey(5, Channel.Channel5, Pitch.F3));
            shots.Add(new shotKey(6, Channel.Channel6, Pitch.F3));
            shots.Add(new shotKey(7, Channel.Channel7, Pitch.F3));
            shots.Add(new shotKey(8, Channel.Channel8, Pitch.F3));

            shots.Add(new shotKey(9,  Channel.Channel1, Pitch.FSharp3));
            shots.Add(new shotKey(10, Channel.Channel2, Pitch.FSharp3));
            shots.Add(new shotKey(11, Channel.Channel3, Pitch.FSharp3));
            shots.Add(new shotKey(12, Channel.Channel4, Pitch.FSharp3));
            shots.Add(new shotKey(13, Channel.Channel5, Pitch.FSharp3));
            shots.Add(new shotKey(14, Channel.Channel6, Pitch.FSharp3));
            shots.Add(new shotKey(15, Channel.Channel7, Pitch.FSharp3));
            shots.Add(new shotKey(16, Channel.Channel8, Pitch.FSharp3));

            shots.Add(new shotKey(17, Channel.Channel1, Pitch.G3));
            shots.Add(new shotKey(18, Channel.Channel2, Pitch.G3));
            shots.Add(new shotKey(19, Channel.Channel3, Pitch.G3));
            shots.Add(new shotKey(20, Channel.Channel4, Pitch.G3));
            shots.Add(new shotKey(21, Channel.Channel5, Pitch.G3));
            shots.Add(new shotKey(22, Channel.Channel6, Pitch.G3));
            shots.Add(new shotKey(23, Channel.Channel7, Pitch.G3));
            shots.Add(new shotKey(24, Channel.Channel8, Pitch.G3));

            shots.Add(new shotKey(25, Channel.Channel1, Pitch.GSharp3));
            shots.Add(new shotKey(26, Channel.Channel2, Pitch.GSharp3));
            shots.Add(new shotKey(27, Channel.Channel3, Pitch.GSharp3));
            shots.Add(new shotKey(28, Channel.Channel4, Pitch.GSharp3));
            shots.Add(new shotKey(29, Channel.Channel5, Pitch.GSharp3));
            shots.Add(new shotKey(30, Channel.Channel6, Pitch.GSharp3));
            shots.Add(new shotKey(31, Channel.Channel7, Pitch.GSharp3));
            shots.Add(new shotKey(32, Channel.Channel8, Pitch.GSharp3));

            shots.Add(new shotKey(33, Channel.Channel1, Pitch.A3));
            shots.Add(new shotKey(34, Channel.Channel2, Pitch.A3));
            shots.Add(new shotKey(35, Channel.Channel3, Pitch.A3));
            shots.Add(new shotKey(36, Channel.Channel4, Pitch.A3));
            shots.Add(new shotKey(37, Channel.Channel5, Pitch.A3));
            shots.Add(new shotKey(38, Channel.Channel6, Pitch.A3));
            shots.Add(new shotKey(39, Channel.Channel7, Pitch.A3));
            shots.Add(new shotKey(40, Channel.Channel8, Pitch.A3));

            shotListThread = new Thread(shotListThreadMethod);
            shotListThread.Start();

            shotStatusThread = new Thread(shotStatusThreadMethod);
            shotStatusThread.Start();
        }
        public void shotListThreadMethod()
        {
            while (true)
            {
                Thread.Sleep(2000);
                string shotListHTML = (new WebClient()).DownloadString("http://127.0.0.1:8088/tally");

                List<LinkItem> matchingLinks = LinkFinder.Find(shotListHTML).Where(link => link.Class == "tallyLink").ToList();

                for (int i=0;i < 40;i++)
                {
                    if (i < matchingLinks.Count())
                        shots[i].guid = new Guid(matchingLinks[i].Href.Replace("/tally/?key=", ""));
                    else
                        shots[i].guid = new Guid();
                }
            }
        }
        public void shotStatusThreadMethod()
        {
            while (true)
            {
                Thread.Sleep(100);
                foreach (shotKey shot in shots.Where(s => s.guid != default(Guid)).ToList())
                {
                    string shotStatusHTML = (new WebClient()).DownloadString("http://127.0.0.1:8088/tallyupdate/?key="+ shot.guid.ToString());
                    if (shotStatusHTML == "tallyChange(\"#1a3c75\");")
                    {
                        shot.live = false;
                        shot.preview = false;
                    }
                    else if (shotStatusHTML == "tallyChange(\"#ff8c00\");")
                    {
                        shot.live = false;
                        shot.preview = true;
                    }
                    else if (shotStatusHTML == "tallyChange(\"#006400\");")
                    {
                        shot.live = true;
                        shot.preview = false;
                    }
                }
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            shotListThread.Abort();
            shotStatusThread.Abort();
        }
    }
    public struct LinkItem
    {
        public string Href;
        public string Text;
        public string Class;

        public override string ToString()
        {
            return Href + "\n\t" + Text;

        }
    }

    static class LinkFinder
    {
        public static List<LinkItem> Find(string file)
        {
            List<LinkItem> list = new List<LinkItem>();

            MatchCollection m1 = Regex.Matches(file, @"(<a.*?>.*?</a>)",
                RegexOptions.Singleline);

            foreach (Match m in m1)
            {
                string value = m.Groups[1].Value;
                LinkItem i = new LinkItem();

                Match m2 = Regex.Match(value, @"href=\""(.*?)\""",
                RegexOptions.Singleline);
                if (m2.Success)
                {
                    i.Href = m2.Groups[1].Value;
                }

                Match m3 = Regex.Match(value, @"class=\""(.*?)\""",
                RegexOptions.Singleline);
                if (m2.Success)
                {
                    i.Class = m3.Groups[1].Value;
                }

                string t = Regex.Replace(value, @"\s*<.*?>\s*", "",
                RegexOptions.Singleline);
                i.Text = t;

                list.Add(i);
            }
            return list;
        }
    }
    public class shotKey
    {
        private Guid _guid;
        public Guid guid {
            get { return _guid; }
            set
            {
                _guid = value;
                if (_guid != default(Guid))
                {
                    if (!preview && !live)
                        this.setValue(5);
                    if (preview)
                        this.setValue(1);
                    if (live)
                        this.setValue(3);
                }
                else
                {
                    this.setValue(0);
                }
            }
        }
        public int shotID;
        public Channel midiChannel;
        public Pitch midiPitch;
        private bool _preview;
        private bool _live;
        public bool preview {
            get { return _preview; }
            set {
                _preview = value;
                if (_guid != default(Guid))
                {
                    if (!preview && !live)
                        this.setValue(5);
                    if (preview)
                        this.setValue(1);
                    if (live)
                        this.setValue(3);
                } else
                {
                    this.setValue(0);
                }
            }
        }
        public bool live
        {
            get { return _live; }
            set
            {
                _live = value;
                if (_guid != default(Guid))
                {
                    if (!preview && !live)
                        this.setValue(5);
                    if (preview)
                        this.setValue(1);
                    if (live)
                        this.setValue(3);
                } else
                {
                    this.setValue(0);
                }
            }
        }
        public shotKey(int id, Channel channel, Pitch pitch)
        {
            midiChannel = channel;
            midiPitch = pitch;
            preview = false;
            live = false;
            shotID = id;
        }

    }
    public static class ExtensionMethods
    {
        public static OutputDevice outputDevice;

        public static void setValue(this shotKey shot, int value)
        {
            outputDevice.SendNoteOn(shot.midiChannel, shot.midiPitch, value);
        }
    }
}


There are no setting, no nothing really, just quick and dirty code.
jhebbel attached the following image(s):
20151028_193834.jpg (2,546kb) downloaded 133 time(s).

You cannot view/download attachments. Try to login or register.
thanks 1 user thanked jhebbel for this useful post.
tdurhamjr on 12/9/2015(UTC)
jhebbel  
#2 Posted : Thursday, October 29, 2015 7:54:45 PM(UTC)
jhebbel

Rank: Advanced Member

Groups: Registered
Joined: 10/28/2015(UTC)
Posts: 183

Thanks: 7 times
Was thanked: 15 time(s) in 14 post(s)
Compiled version for those without an IDE
File Attachment(s):
vMix-APC40.zip (31kb) downloaded 170 time(s).

You cannot view/download attachments. Try to login or register.
tdurhamjr  
#3 Posted : Wednesday, December 9, 2015 8:20:51 AM(UTC)
tdurhamjr

Rank: Advanced Member

Groups: Registered
Joined: 9/8/2014(UTC)
Posts: 244
Location: Southaven MS

Thanks: 12 times
Was thanked: 25 time(s) in 21 post(s)
@jhebbel,
Do you happen to have code that will work with the APC Mini? If not, I guess I could just buy and APC 40.

Thanks for the post on this!!
Tim
jhebbel  
#4 Posted : Wednesday, December 9, 2015 8:33:32 AM(UTC)
jhebbel

Rank: Advanced Member

Groups: Registered
Joined: 10/28/2015(UTC)
Posts: 183

Thanks: 7 times
Was thanked: 15 time(s) in 14 post(s)
tdurhamjr wrote:
@jhebbel,
Do you happen to have code that will work with the APC Mini? If not, I guess I could just buy and APC 40.

Thanks for the post on this!!
Tim


You can dump the OutputDevice.InstalledDevices object to find the "name" of the device, then use the graphic below to find the notes and velocities.
UserPostedImage
APVTV  
#5 Posted : Sunday, April 17, 2016 4:36:09 AM(UTC)
APVTV

Rank: Newbie

Groups: Registered
Joined: 4/15/2016(UTC)
Posts: 2
Location: RF

Friends, please help me! I'm not a programmer! I directed live broadcasts, the project is now on sports. I need to configure APC 40 MK II that would be under the my tasks button glowed the appropriate color. For example - the top line is the program, where the selected button is lit red. The second line of the preview, there is selected the button is lit green. Bottom line - the credits, there is lit purple. Ideal - if the whole top line of buttons is illuminated with red light, and the selected channel is bright red. Ready to pay your work completely.
jhebbel  
#6 Posted : Sunday, April 17, 2016 8:07:24 AM(UTC)
jhebbel

Rank: Advanced Member

Groups: Registered
Joined: 10/28/2015(UTC)
Posts: 183

Thanks: 7 times
Was thanked: 15 time(s) in 14 post(s)
I wish I had a MK II so I could help out, but without the device in front of me, its hard to develop for. Surely there must be somebody who has one and has c# experience that can work with the code I posted.
jhebbel  
#7 Posted : Sunday, April 17, 2016 1:08:48 PM(UTC)
jhebbel

Rank: Advanced Member

Groups: Registered
Joined: 10/28/2015(UTC)
Posts: 183

Thanks: 7 times
Was thanked: 15 time(s) in 14 post(s)
Sweet, im going to get into this next week. Ive been writing a piece of software called inputmapper which has the purpose of connecting and using devices that they weren't specifically designed for.

An example is here of me using a audio spectrum input plugin and the APC 40 output plugin to make a visual spectrium that I demo here:


I intend to make a vMix plugin and with this info I can make a APC 40 MKII plugin as well.
madness  
#8 Posted : Monday, April 18, 2016 4:36:10 PM(UTC)
madness

Rank: Advanced Member

Groups: Registered
Joined: 4/16/2013(UTC)
Posts: 406
Man
Location: Iowa

Thanks: 281 times
Was thanked: 32 time(s) in 29 post(s)
BETech wrote:
Alternatively, another way of transmitting MIDI messages to the outboard control surface, would be via the vMix Shortcuts menu. By adding the option of “MIDITXData” to the shortcut’s list, when the User presses a button and releases, a Midi message/s could be sent with hexadecimal code (NoteOn, Midi Channel, Message & Value) to the device. Each command could be entered in sequence into the text box within the Shortcut’s dialog box or form. In the example below, the operator presses Pad Number 3 on the APC40 (designated as the Preview Bus with 8 buttons assigned), and the Pad lights-up, and also extinguishes the other 7 Pad LEDS at the same time.

UserPostedImage


I like this idea! With your image, I thought it was already part of vMix, but after looking for it, I realize you are wanting this capability, and this was likely a mach up of what you desire.

In the OtsAV Midi Mapping Language, they configure lights with special code.

They initialize the LEDs with the following to assign the lights to variables for readability
!blink[~var_name, M,MIDI:off:code,MIDI:on:code]
Example:
!blink[~deck_a_playpause_led,M,0xB0:0x4B:0x27,0xB0:0x4A:0x27]

That is an example for the Denon DN­HD2500 DJ MIDI Hardware Controller deck A play pause button.

Then they call it with something like the following.
BlinkSet(~var_name,x)

The x...
x = 0 (off) (bit: 0000)
x = 1 (on) (bit: 0001)
x = 2 (slow blink) (bit: 0010)
x = 3 (medium blink) (bit: 0011)
x = 4 (fast blink) (bit: 0100)
x .... x = 12 (inverse fast blink) (bit: 1100)

like so...
BlinkSet(deck_a_playpause_led,2)
Causing a slow blink situation.

ichn1  
#9 Posted : Tuesday, July 5, 2016 7:53:32 PM(UTC)
ichn1

Rank: Advanced Member

Groups: Registered
Joined: 6/25/2016(UTC)
Posts: 61

Thanks: 1 times
Was thanked: 5 time(s) in 3 post(s)
BETech wrote:
Alternatively, another way of transmitting MIDI messages to the outboard control surface, would be via the vMix Shortcuts menu. By adding the option of “MIDITXData” to the shortcut’s list, when the User presses a button and releases, a Midi message/s could be sent with hexadecimal code (NoteOn, Midi Channel, Message & Value) to the device. Each command could be entered in sequence into the text box within the Shortcut’s dialog box or form. In the example below, the operator presses Pad Number 3 on the APC40 (designated as the Preview Bus with 8 buttons assigned), and the Pad lights-up, and also extinguishes the other 7 Pad LEDS at the same time.

UserPostedImage



i cant find MIDITXData where to find it , how please
ichn1  
#10 Posted : Sunday, July 10, 2016 3:27:58 PM(UTC)
ichn1

Rank: Advanced Member

Groups: Registered
Joined: 6/25/2016(UTC)
Posts: 61

Thanks: 1 times
Was thanked: 5 time(s) in 3 post(s)
jhebbel wrote:
Compiled version for those without an IDE

please dear . can you comply a version for akai apc mini please . i am not a programer. any one can help please. also i recommend this request to be available in vmix as native. thanks a lot for everything
thanks 1 user thanked ichn1 for this useful post.
corporatejames on 7/10/2016(UTC)
corporatejames  
#11 Posted : Sunday, July 10, 2016 11:36:51 PM(UTC)
corporatejames

Rank: Advanced Member

Groups: Registered
Joined: 8/2/2015(UTC)
Posts: 364
Man
Australia
Location: Sydney

Thanks: 283 times
Was thanked: 76 time(s) in 62 post(s)
ichn1 wrote:
jhebbel wrote:
Compiled version for those without an IDE

please dear . can you comply a version for akai apc mini please . i am not a programer. any one can help please. also i recommend this request to be available in vmix as native. thanks a lot for everything



I would like one for APC Mini as well
stigaard  
#12 Posted : Monday, July 11, 2016 9:22:53 AM(UTC)
stigaard

Rank: Advanced Member

Groups: Registered
Joined: 5/20/2015(UTC)
Posts: 493
Man
Denmark
Location: Copenhagen, Denmark

Thanks: 398 times
Was thanked: 100 time(s) in 79 post(s)
corporatejames wrote:
ichn1 wrote:
jhebbel wrote:
Compiled version for those without an IDE

please dear . can you comply a version for akai apc mini please . i am not a programer. any one can help please. also i recommend this request to be available in vmix as native. thanks a lot for everything



I would like one for APC Mini as well


+1

I would really like this too :-)
stigaard  
#13 Posted : Monday, July 11, 2016 12:57:28 PM(UTC)
stigaard

Rank: Advanced Member

Groups: Registered
Joined: 5/20/2015(UTC)
Posts: 493
Man
Denmark
Location: Copenhagen, Denmark

Thanks: 398 times
Was thanked: 100 time(s) in 79 post(s)
For the ones looking for a working code, on the Akai APC Mini:

Replace all the lines in the method Form1 from the code from the code initially posted looking like this:

Code:
shots.Add(new shotKey(X, Channel.ChannelX, Pitch.XXX));


With the following code:
Code:
shots = new List<ShotKey>();

// MIDI controller - Button number offsets
int OFFSET_1ST_LINE = 56 - 1;
int OFFSET_2ND_LINE = 48 - 1;
// Bind input (shots) to a button on the MIDI controller
for (int i = 1; i <= 8; i++)
{
   shots.Add(new ShotKey(i, Channel.Channel1, (Pitch)OFFSET_1ST_LINE + i));
}
for (int i = 9; i <= 16; i++)
{
   shots.Add(new ShotKey(i, Channel.Channel1, (Pitch)OFFSET_2ND_LINE + i));
}



This works for visual feedback of the first 16 inputs vMix, on the two upper rows of buttons on the APC Mini.


EDIT: And remember to change the device name to search for, to APC MINI.
ichn1  
#14 Posted : Monday, July 11, 2016 9:42:53 PM(UTC)
ichn1

Rank: Advanced Member

Groups: Registered
Joined: 6/25/2016(UTC)
Posts: 61

Thanks: 1 times
Was thanked: 5 time(s) in 3 post(s)
hello and thanks for the help, i dont know how to make it exe im not a programer. is it possible you can drop a complied one please for apc mini.
thanks
stigaard  
#15 Posted : Tuesday, July 12, 2016 7:32:23 AM(UTC)
stigaard

Rank: Advanced Member

Groups: Registered
Joined: 5/20/2015(UTC)
Posts: 493
Man
Denmark
Location: Copenhagen, Denmark

Thanks: 398 times
Was thanked: 100 time(s) in 79 post(s)
ichn1 wrote:
hello and thanks for the help, i dont know how to make it exe im not a programer. is it possible you can drop a complied one please for apc mini.
thanks


Hi ichn1, I'll try later today to compile it to a exe :-)
ichn1  
#16 Posted : Wednesday, July 13, 2016 3:33:46 AM(UTC)
ichn1

Rank: Advanced Member

Groups: Registered
Joined: 6/25/2016(UTC)
Posts: 61

Thanks: 1 times
Was thanked: 5 time(s) in 3 post(s)
stigaard wrote:
ichn1 wrote:
hello and thanks for the help, i dont know how to make it exe im not a programer. is it possible you can drop a complied one please for apc mini.
thanks


Hi ichn1, I'll try later today to compile it to a exe :-)



waiting

thanks a lot

stigaard  
#17 Posted : Wednesday, July 13, 2016 7:56:18 AM(UTC)
stigaard

Rank: Advanced Member

Groups: Registered
Joined: 5/20/2015(UTC)
Posts: 493
Man
Denmark
Location: Copenhagen, Denmark

Thanks: 398 times
Was thanked: 100 time(s) in 79 post(s)
ichn1 wrote:

waiting

thanks a lot



Hi ichn1, I'm currently working on a enhanced program that enables both Akai APC Mini and Akai MIDI Mix in use.

I'm expected the program to be released tonight, or perhaps tomorrow. Hope the waiting will meet the expectations.
thanks 1 user thanked stigaard for this useful post.
corporatejames on 7/13/2016(UTC)
ichn1  
#18 Posted : Thursday, July 14, 2016 4:15:00 AM(UTC)
ichn1

Rank: Advanced Member

Groups: Registered
Joined: 6/25/2016(UTC)
Posts: 61

Thanks: 1 times
Was thanked: 5 time(s) in 3 post(s)
stigaard wrote:
ichn1 wrote:

waiting

thanks a lot



Hi ichn1, I'm currently working on a enhanced program that enables both Akai APC Mini and Akai MIDI Mix in use.

I'm expected the program to be released tonight, or perhaps tomorrow. Hope the waiting will meet the expectations.





thanks a lot. waiting your software
ichn1  
#19 Posted : Saturday, July 16, 2016 1:38:04 AM(UTC)
ichn1

Rank: Advanced Member

Groups: Registered
Joined: 6/25/2016(UTC)
Posts: 61

Thanks: 1 times
Was thanked: 5 time(s) in 3 post(s)
any news ?/


thanks a lot
ichn1  
#20 Posted : Wednesday, July 20, 2016 11:13:34 AM(UTC)
ichn1

Rank: Advanced Member

Groups: Registered
Joined: 6/25/2016(UTC)
Posts: 61

Thanks: 1 times
Was thanked: 5 time(s) in 3 post(s)
any updates please
Users browsing this topic
Guest (4)
2 Pages12>
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.