From c91b4d56a70b9c85a9141fa933fad37d29b21b76 Mon Sep 17 00:00:00 2001 From: JulioEI Date: Tue, 31 May 2022 12:15:21 +0200 Subject: [PATCH] channel numbering now starts with 1 --- README.md | 10 +++--- Source/ChInterp.cpp | 20 +++++------ Source/ChInterp.h | 4 +-- Source/ChInterpEditor.cpp | 70 +++++++++++++++++++------------------- ch-interp-plugin.png | Bin 5125 -> 5674 bytes 5 files changed, 53 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index aef6f1a..af8cb5c 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,16 @@ For Windows: The plugin receives N channels as input (2<=N<=7), and interpolate the missing ones with a weighted linear approximation. ![CNN-ripple](ch-interp-plugin.png) -- **Source Channels:** indicates the channels used as a source (>=0) and the desired interpolated ones (-1). Source channels (>=0) will be indicated in orange, whereas the ones that need to be interpolated must be indicated with a -1 (the box will turn grey). +- **Source Channels:** indicates the channels used as a source (>=1) and the desired interpolated ones (-1). Source channels (>=1) will be indicated in orange, whereas the ones that need to be interpolated must be indicated with a -1 (the box will turn grey). Note that channel numbering starts at 1. - **Recipient Channels:** indicates the channels where the source channels will be saved (one to one mapping). -For instance, in the image provided, the plugin takes channels (0,4,12) and uses them to interpolate the missing 5 channels (2 interpolations between 0&4, 3 interpolations between 4&12). The resulting 8 channels (3 provided, 5 interpolated) are saved in the 0-8 channels. +For instance, in the image provided, the plugin takes channels (1,5,12) and uses them to interpolate the missing 5 channels (2 interpolations between 1&5, 3 interpolations between 5&12). The resulting 8 channels (3 provided, 5 interpolated) are saved in the 1-8 channels. + +Note that channels index refers to the practical order. If a Channel Map plugin is used to rearrange the channels, the new order is passed down to this plugin. That is, if a channel map is used to place channel 18 into the first (1) position, then if we want to access that channel for interpolation, we must refer to it in the source channels with index 1. ## Compiling the plugin from source -1. Clone this repository in the same directory where [`plugin-GUI`](https://github.com/open-ephys/plugin-GUI) is located. +1. Clone this repository inside a folder `OEPlugins` in the same directory where [`plugin-GUI`](https://github.com/open-ephys/plugin-GUI) is located. 3. Go to the `Build` directory (/Build/) and execute to create the CMake project: For Linux: @@ -35,7 +37,7 @@ make install For Windows: ``` -cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_CXX_FLAGS="/EHsc /utf-8" .. +cmake -G "Visual Studio 16 2019" -A x64 .. ``` Open Visual Studio 16 2019 and compile and install the solution (see notes below on how to install Visual Studio). diff --git a/Source/ChInterp.cpp b/Source/ChInterp.cpp index fd81d6b..1ca3b5f 100644 --- a/Source/ChInterp.cpp +++ b/Source/ChInterp.cpp @@ -6,8 +6,8 @@ using namespace ChInterpSpace; //Change all names for the relevant ones, including "Processor Name" ChInterp::ChInterp() : GenericProcessor("Ch-interp") { - int og_channelArray[8] = {0,-1,-1,3,-1,-1,-1,7}; - int to_channelArray[8] = {0,1,2,3,4,5,6,7}; + int og_channelArray[8] = {1,-1,-1,4,-1,-1,-1,8}; + int to_channelArray[8] = {1,2,3,4,5,6,7,8}; } @@ -69,20 +69,20 @@ void ChInterp::process(AudioSampleBuffer& buffer) for (int ch=0; ch<8; ++ch) { - if (og_channelArray[ch]> -1) //if channel provided then just reallocate on new place + if (og_channelArray[ch]> 0) //if channel provided then just reallocate on new place { - int nSamples = getNumSamples(og_channelArray[ch]); - float* toPtr = buffer.getWritePointer(to_channelArray[ch]); //get pointer to new location + int nSamples = getNumSamples(og_channelArray[ch]-1); + float* toPtr = buffer.getWritePointer(to_channelArray[ch]-1); //get pointer to new location for (int n = 0; n < nSamples; ++n) { - const float sample = *channelBuffer.getReadPointer (og_channelArray[ch], n); //get value from old location + const float sample = *channelBuffer.getReadPointer (og_channelArray[ch]-1, n); //get value from old location *(toPtr + n) = sample; //save value to new location } } else //if channel not provided then interpolate { - int nSamples = getNumSamples(to_channelArray[ch]); - float* toPtr = buffer.getWritePointer(to_channelArray[ch]); //get pointer to memory space to save the interpolated channel + int nSamples = getNumSamples(to_channelArray[ch]-1); + float* toPtr = buffer.getWritePointer(to_channelArray[ch]-1); //get pointer to memory space to save the interpolated channel int left_ch = leftIndex(og_channelArray, ch); //get closest provided channel on the left int right_ch = rightIndex(og_channelArray, ch); //get closest provided channel on the right @@ -90,8 +90,8 @@ void ChInterp::process(AudioSampleBuffer& buffer) for (int n=0; nsetColour(Label::textColourId, Colours::white); addAndMakeVisible(ogChannel_label); - ogChannel_0 = new Label("Og Channel 0", "0"); + ogChannel_0 = new Label("Og Channel 1", "1"); ogChannel_0->setBounds(10,45,20,15); ogChannel_0->setFont(Font("Default", 8, Font::plain)); ogChannel_0->setColour(Label::textColourId, Colours::white); ogChannel_0->setColour(Label::backgroundColourId, Colours::orange); ogChannel_0->setEditable(true); ogChannel_0->addListener(this); - ogChannel_0->setTooltip("Set the source channel 0 for interpolation"); + ogChannel_0->setTooltip("Set the source channel 1 for interpolation"); addAndMakeVisible(ogChannel_0); - ogChannel_1 = new Label("Og Channel 1", "-1"); + ogChannel_1 = new Label("Og Channel 2", "-1"); ogChannel_1->setBounds(32,45,20,15); ogChannel_1->setFont(Font("Default", 8, Font::plain)); ogChannel_1->setColour(Label::textColourId, Colours::black); ogChannel_1->setColour(Label::backgroundColourId, Colours::grey); ogChannel_1->setEditable(true); ogChannel_1->addListener(this); - ogChannel_1->setTooltip("Set the source channel 1 for interpolation"); + ogChannel_1->setTooltip("Set the source channel 2 for interpolation"); addAndMakeVisible(ogChannel_1); - ogChannel_2 = new Label("Og Channel 2", "-1"); + ogChannel_2 = new Label("Og Channel 3", "-1"); ogChannel_2->setBounds(54,45,20,15); ogChannel_2->setFont(Font("Default", 8, Font::plain)); ogChannel_2->setColour(Label::textColourId, Colours::black); ogChannel_2->setColour(Label::backgroundColourId, Colours::grey); ogChannel_2->setEditable(true); ogChannel_2->addListener(this); - ogChannel_2->setTooltip("Set the source channel 2 for interpolation"); + ogChannel_2->setTooltip("Set the source channel 3 for interpolation"); addAndMakeVisible(ogChannel_2); - ogChannel_3 = new Label("Og Channel 3", "3"); + ogChannel_3 = new Label("Og Channel 4", "4"); ogChannel_3->setBounds(76,45,20,15); ogChannel_3->setFont(Font("Default", 8, Font::plain)); ogChannel_3->setColour(Label::textColourId, Colours::white); ogChannel_3->setColour(Label::backgroundColourId, Colours::orange); ogChannel_3->setEditable(true); ogChannel_3->addListener(this); - ogChannel_3->setTooltip("Set the source channel 1 for interpolation"); + ogChannel_3->setTooltip("Set the source channel 4 for interpolation"); addAndMakeVisible(ogChannel_3); - ogChannel_4 = new Label("Og Channel 4", "-1"); + ogChannel_4 = new Label("Og Channel 5", "-1"); ogChannel_4->setBounds(98,45,20,15); ogChannel_4->setFont(Font("Default", 8, Font::plain)); ogChannel_4->setColour(Label::textColourId, Colours::black); ogChannel_4->setColour(Label::backgroundColourId, Colours::grey); ogChannel_4->setEditable(true); ogChannel_4->addListener(this); - ogChannel_4->setTooltip("Set the source channel 4 for interpolation"); + ogChannel_4->setTooltip("Set the source channel 5 for interpolation"); addAndMakeVisible(ogChannel_4); - ogChannel_5 = new Label("Og Channel 5", "-1"); + ogChannel_5 = new Label("Og Channel 6", "-1"); ogChannel_5->setBounds(120,45,20,15); ogChannel_5->setFont(Font("Default", 8, Font::plain)); ogChannel_5->setColour(Label::textColourId, Colours::black); ogChannel_5->setColour(Label::backgroundColourId, Colours::grey); ogChannel_5->setEditable(true); ogChannel_5->addListener(this); - ogChannel_5->setTooltip("Set the source channel 5 for interpolation"); + ogChannel_5->setTooltip("Set the source channel 6 for interpolation"); addAndMakeVisible(ogChannel_5); - ogChannel_6 = new Label("Og Channel 6", "-1"); + ogChannel_6 = new Label("Og Channel 7", "-1"); ogChannel_6->setBounds(142,45,20,15); ogChannel_6->setFont(Font("Default", 8, Font::plain)); ogChannel_6->setColour(Label::textColourId, Colours::black); ogChannel_6->setColour(Label::backgroundColourId, Colours::grey); ogChannel_6->setEditable(true); ogChannel_6->addListener(this); - ogChannel_6->setTooltip("Set the source channel 6 for interpolation"); + ogChannel_6->setTooltip("Set the source channel 7 for interpolation"); addAndMakeVisible(ogChannel_6); - ogChannel_7 = new Label("Og Channel 7", "7"); + ogChannel_7 = new Label("Og Channel 8", "8"); ogChannel_7->setBounds(164,45,20,15); ogChannel_7->setFont(Font("Default", 8, Font::plain)); ogChannel_7->setColour(Label::textColourId, Colours::white); ogChannel_7->setColour(Label::backgroundColourId, Colours::orange); ogChannel_7->setEditable(true); ogChannel_7->addListener(this); - ogChannel_7->setTooltip("Set the source channel 7 for interpolation"); + ogChannel_7->setTooltip("Set the source channel 8 for interpolation"); addAndMakeVisible(ogChannel_7); @@ -100,84 +100,84 @@ ChInterpEditor::ChInterpEditor(GenericProcessor* parentNode, bool useDefaultPara toChannel_label->setColour(Label::textColourId, Colours::white); addAndMakeVisible(toChannel_label); - toChannel_0 = new Label("To Channel 0", "0"); + toChannel_0 = new Label("To Channel 1", "1"); toChannel_0->setBounds(10,100,20,15); toChannel_0->setFont(Font("Default", 8, Font::plain)); toChannel_0->setColour(Label::textColourId, Colours::white); toChannel_0->setColour(Label::backgroundColourId, Colours::orange); toChannel_0->setEditable(true); toChannel_0->addListener(this); - toChannel_0->setTooltip("Set the recipient channel 0 for interpolation"); + toChannel_0->setTooltip("Set the recipient channel 1 for interpolation"); addAndMakeVisible(toChannel_0); - toChannel_1 = new Label("To Channel 1", "1"); + toChannel_1 = new Label("To Channel 2", "2"); toChannel_1->setBounds(32,100,20,15); toChannel_1->setFont(Font("Default", 8, Font::plain)); toChannel_1->setColour(Label::textColourId, Colours::white); toChannel_1->setColour(Label::backgroundColourId, Colours::orange); toChannel_1->setEditable(true); toChannel_1->addListener(this); - toChannel_1->setTooltip("Set the recipient channel 1 for interpolation"); + toChannel_1->setTooltip("Set the recipient channel 2 for interpolation"); addAndMakeVisible(toChannel_1); - toChannel_2 = new Label("To Channel 2", "2"); + toChannel_2 = new Label("To Channel 3", "3"); toChannel_2->setBounds(54,100,20,15); toChannel_2->setFont(Font("Default", 8, Font::plain)); toChannel_2->setColour(Label::textColourId, Colours::white); toChannel_2->setColour(Label::backgroundColourId, Colours::orange); toChannel_2->setEditable(true); toChannel_2->addListener(this); - toChannel_2->setTooltip("Set the recipient channel 2 for interpolation"); + toChannel_2->setTooltip("Set the recipient channel 3 for interpolation"); addAndMakeVisible(toChannel_2); - toChannel_3 = new Label("To Channel 3", "3"); + toChannel_3 = new Label("To Channel 4", "4"); toChannel_3->setBounds(76,100,20,15); toChannel_3->setFont(Font("Default", 8, Font::plain)); toChannel_3->setColour(Label::textColourId, Colours::white); toChannel_3->setColour(Label::backgroundColourId, Colours::orange); toChannel_3->setEditable(true); toChannel_3->addListener(this); - toChannel_3->setTooltip("Set the recipient channel 1 for interpolation"); + toChannel_3->setTooltip("Set the recipient channel 4 for interpolation"); addAndMakeVisible(toChannel_3); - toChannel_4 = new Label("To Channel 4", "4"); + toChannel_4 = new Label("To Channel 5", "5"); toChannel_4->setBounds(98,100,20,15); toChannel_4->setFont(Font("Default", 8, Font::plain)); toChannel_4->setColour(Label::textColourId, Colours::white); toChannel_4->setColour(Label::backgroundColourId, Colours::orange); toChannel_4->setEditable(true); toChannel_4->addListener(this); - toChannel_4->setTooltip("Set the recipient channel 4 for interpolation"); + toChannel_4->setTooltip("Set the recipient channel 5 for interpolation"); addAndMakeVisible(toChannel_4); - toChannel_5 = new Label("To Channel 5", "5"); + toChannel_5 = new Label("To Channel 6", "6"); toChannel_5->setBounds(120,100,20,15); toChannel_5->setFont(Font("Default", 8, Font::plain)); toChannel_5->setColour(Label::textColourId, Colours::white); toChannel_5->setColour(Label::backgroundColourId, Colours::orange); toChannel_5->setEditable(true); toChannel_5->addListener(this); - toChannel_5->setTooltip("Set the recipient channel 5 for interpolation"); + toChannel_5->setTooltip("Set the recipient channel 6 for interpolation"); addAndMakeVisible(toChannel_5); - toChannel_6 = new Label("To Channel 6", "6"); + toChannel_6 = new Label("To Channel 7", "7"); toChannel_6->setBounds(142,100,20,15); toChannel_6->setFont(Font("Default", 8, Font::plain)); toChannel_6->setColour(Label::textColourId, Colours::white); toChannel_6->setColour(Label::backgroundColourId, Colours::orange); toChannel_6->setEditable(true); toChannel_6->addListener(this); - toChannel_6->setTooltip("Set the recipient channel 6 for interpolation"); + toChannel_6->setTooltip("Set the recipient channel 7 for interpolation"); addAndMakeVisible(toChannel_6); - toChannel_7 = new Label("To Channel 7", "7"); + toChannel_7 = new Label("To Channel 8", "8"); toChannel_7->setBounds(164,100,20,15); toChannel_7->setFont(Font("Default", 8, Font::plain)); toChannel_7->setColour(Label::textColourId, Colours::white); toChannel_7->setColour(Label::backgroundColourId, Colours::orange); toChannel_7->setEditable(true); toChannel_7->addListener(this); - toChannel_7->setTooltip("Set the recipient channel 7 for interpolation"); + toChannel_7->setTooltip("Set the recipient channel 8 for interpolation"); addAndMakeVisible(toChannel_7); } @@ -201,7 +201,7 @@ void ChInterpEditor::labelTextChanged(Label* label) //if change ogChannel if (label == ogChannel_0) { - if (requestedValue<0) + if (requestedValue<=0) { CoreServices::sendStatusMessage("Left most channel must be provided."); return; @@ -234,7 +234,7 @@ void ChInterpEditor::labelTextChanged(Label* label) } else if (label == ogChannel_7) { - if (requestedValue<0) + if (requestedValue<=0) { CoreServices::sendStatusMessage("Right most channel must be provided."); return; @@ -244,7 +244,7 @@ void ChInterpEditor::labelTextChanged(Label* label) else { Array chans = getActiveChannels(); - if (requestedValue<0) + if (requestedValue<=0) { CoreServices::sendStatusMessage("Recipient channel must be a valid number."); return; diff --git a/ch-interp-plugin.png b/ch-interp-plugin.png index 7d1ec04006e0a18b4f1b0d14b621b958be33f79e..2763ded630123469687aab66aa7a42c9994444ca 100644 GIT binary patch literal 5674 zcmb`L2{e>%`^QJhnq)5qm8E1$YHUel*CetJF^wN##?l~UNCqJzlx-~8vyBlmmNGPw z%9boMNNCDBsB8^}|LA?+-~0Q&@A;qqdCz(0oM)cvd9HKc*L~m5eSNRb^Tc1fYQo1O z&I14d_{>ZVty$-F)=|vGvG;E?aE^6iLt2{{0C2s)Io8E~PkpdH08pFEyA3_Sy5{yb zbwmOHf`NMn+h-5y5CA}=)yz=eCgjFqt`{6=65PqB1bRE2$gat8-$lZ7nD}+<%;d2| z+2H5Kld2N3`Z>pT++VkGm_E9ks1!c2!L78fMq}MPUCEJC!TP1Fd9kUBu#t74X-evq z__IdwAhY;pOY`9=9@n*(c0nsWfjz5g){NbI(wZytL9G$CYs(c!Q`ITVvDV$Pu;pl1 zHUMxc9pYz8>Dla19g34i9R&EJ$2_{xcs74NE(ri<6w5fgXDFCa^SkpZn;X_Em@=-6 za5cPztu2M)3vq3&ay3&6p_^h8dq^8(L0BR zRVgHO≺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 ag{ls}c{Rz_QQ#LHNL^X;amhpT(Ek8~y((k?