From 6846195546e53e249e0c65e1153b539a5a6ebc85 Mon Sep 17 00:00:00 2001
From: Aaron Cuevas Lopez <aacuelo@teleco.upv.es>
Date: Tue, 4 Nov 2014 01:17:02 +0100
Subject: [PATCH] Extend FileReader functionality

---
 Source/Processors/FileReader/FileReader.cpp   | 199 ++++++++++------
 Source/Processors/FileReader/FileReader.h     |  23 +-
 .../FileReader/FileReaderEditor.cpp           | 222 +++++++++++++++++-
 .../Processors/FileReader/FileReaderEditor.h  |  43 +++-
 Source/Processors/FileReader/FileSource.cpp   |   9 +-
 Source/Processors/FileReader/FileSource.h     |   4 +-
 .../Processors/FileReader/KwikFileSource.cpp  |  12 +
 Source/Processors/FileReader/KwikFileSource.h |   2 +
 8 files changed, 417 insertions(+), 97 deletions(-)

diff --git a/Source/Processors/FileReader/FileReader.cpp b/Source/Processors/FileReader/FileReader.cpp
index f3e55b804..6fb43b04b 100644
--- a/Source/Processors/FileReader/FileReader.cpp
+++ b/Source/Processors/FileReader/FileReader.cpp
@@ -25,23 +25,21 @@
 #include "FileReaderEditor.h"
 #include <stdio.h>
 
+#include "KwikFileSource.h"
+
 FileReader::FileReader()
     : GenericProcessor("File Reader")
 {
 
-    input = 0;
     timestamp = 0;
 
     enabledState(false);
 
-    counter = 0;
 
 }
 
 FileReader::~FileReader()
 {
-    if (input)
-        fclose(input);
 }
 
 AudioProcessorEditor* FileReader::createEditor()
@@ -54,7 +52,7 @@ AudioProcessorEditor* FileReader::createEditor()
 
 bool FileReader::isReady()
 {
-    if (input == 0)
+    if (input == nullptr)
     {
         sendActionMessage("No file selected in File Reader.");
         return false;
@@ -68,17 +66,26 @@ bool FileReader::isReady()
 
 float FileReader::getDefaultSampleRate()
 {
-    return 40000.0f;
+    if (input)
+		return currentSampleRate;
+	else
+		return 44100.0;
 }
 
 int FileReader::getDefaultNumOutputs()
 {
-    return 16;
+	if (input)
+		return currentNumChannels;
+	else
+		return 16;
 }
 
 float FileReader::getBitVolts(int chan)
 {
-    return 0.05f;
+	if (input)
+		return channelInfo[chan].bitVolts;
+	else
+		return 0.05f;
 }
 
 void FileReader::enabledState(bool t)
@@ -89,42 +96,73 @@ void FileReader::enabledState(bool t)
 }
 
 
-void FileReader::setFile(String fullpath)
+bool FileReader::setFile(String fullpath)
 {
-
-    filePath = fullpath;
-
-    const char* path = filePath.getCharPointer();
-
-    if (input)
-        fclose(input);
-
-    input = fopen(path, "rb");
-
-    // Avoid a segfault if file isn't found
-    if (!input)
-    {
-        std::cout << "Can't find data file "
-                  << '"' << path << "\""
-                  << std::endl;
-        return;
-    }
-
-    fseek(input, 0, SEEK_END);
-    lengthOfInputFile = ftell(input);
-    rewind(input);
-
+	File file(fullpath);
+
+	String ext = file.getFileExtension();
+
+	if (!ext.compareIgnoreCase(".kwd"))
+	{
+		input = new KWIKFileSource();
+	}
+	else 
+	{
+		sendActionMessage("File type not supported");
+		return false;
+	}
+
+	if (!input->OpenFile(file))
+	{
+		input = nullptr;
+		sendActionMessage("Invalid file");
+		return false;
+	}
+
+	if (input->getNumRecords() <= 0)
+	{
+		input = nullptr;
+		sendActionMessage("Empty file. Inoring open operation");
+		return false;
+	}
+	static_cast<FileReaderEditor*>(getEditor())->populateRecordings(input);
+	setActiveRecording(0);
 }
 
+void FileReader::setActiveRecording(int index)
+{
+	input->setActiveRecord(index);
+	currentNumChannels = input->getActiveNumChannels();
+	currentNumSamples = input->getActiveNumSamples();
+	currentSampleRate = input->getActiveSampleRate();
+	startSample = 0;
+	stopSample = currentNumSamples;
+	currentSample = 0;
+	for (int i=0; i < currentNumChannels; i++)
+	{
+		channelInfo.add(input->getChannelInfo(i));
+	}
+	static_cast<FileReaderEditor*>(getEditor())->setTotalTime(samplesToMilliseconds(currentNumSamples));
+	readBuffer.malloc(currentNumChannels*BUFFER_SIZE);
+}
 
 String FileReader::getFile()
 {
-    return filePath;
+    if (input)
+		return input->getFileName();
+	else
+		return String::empty;
 }
 
 void FileReader::updateSettings()
 {
+	if (!input) return;
 
+	for (int i=0; i < currentNumChannels; i++)
+	{
+		channels[i]->bitVolts = channelInfo[i].bitVolts;
+		channels[i]->name = channelInfo[i].name;
+	}
 }
 
 
@@ -167,49 +205,33 @@ void FileReader::process(AudioSampleBuffer& buffer, MidiBuffer& events, int& nSa
 
     int samplesNeeded = (int) float(buffer.getNumSamples()) * (getDefaultSampleRate()/44100.0f);
 
-
-    // if (counter == 0)
-    // {
-    //     samplesNeeded = samplesNeeded - 2;
-    //     counter = 1;
-    // } else {
-    //     samplesNeeded = samplesNeeded + 2;
-    //     counter = 0;
-    // }
-
-    if (ftell(input) >= lengthOfInputFile - samplesNeeded)
-    {
-        rewind(input);
-    }
-
-    size_t numRead = fread(readBuffer, 2, samplesNeeded*buffer.getNumChannels(), input);
-
-    int chan = 0;
-    int samp = 0;
-
-    for (size_t n = 0; n < numRead; n++)
-    {
-
-        if (chan == buffer.getNumChannels())
-        {
-            samp++;
-            timestamp++;
-            chan = 0;
-        }
-
-        int16 sample = readBuffer[n];
-
-#ifdef JUCE_WINDOWS //-- big-endian format
-        // reverse the byte order
-        //	float sample_f;
-        //	AudioDataConverters::convertInt16BEToFloat(&readBuffer[n], &sample_f, 1);
-
-#endif
-
-        *buffer.getWritePointer(chan++, samp) = -sample * getDefaultBitVolts();
-
-    }
-
+    int samplesReaded = 0;
+
+	while (samplesReaded < samplesNeeded)
+	{
+		int samplesToRead = samplesNeeded - samplesReaded;
+		if ((currentSample + samplesToRead) > stopSample)
+		{
+			samplesToRead = stopSample - currentSample;
+			if (samplesToRead > 0)
+				input->readData(readBuffer+samplesReaded,samplesToRead);
+			input->seekTo(startSample);
+			currentSample = startSample;
+		}
+		else
+		{
+			input->readData(readBuffer+samplesReaded,samplesToRead);
+			currentSample += samplesToRead;
+		}
+		samplesReaded += samplesToRead;
+	}
+	for (int i=0; i < currentNumChannels; i++)
+	{
+		input->processChannelData(readBuffer,buffer.getWritePointer(i,0),i,samplesNeeded);
+	}
+
+	timestamp += samplesNeeded;
+	static_cast<FileReaderEditor*>(getEditor())->setCurrentTime(samplesToMilliseconds(currentSample));
     nSamples = samplesNeeded;
 
 }
@@ -217,10 +239,33 @@ void FileReader::process(AudioSampleBuffer& buffer, MidiBuffer& events, int& nSa
 
 void FileReader::setParameter(int parameterIndex, float newValue)
 {
-
+	switch (parameterIndex)
+	{
+	case 0: //Change selected recording
+		setActiveRecording(newValue);
+		break;
+	case 1: //set startTime
+		startSample = millisecondsToSamples(newValue);
+		currentSample = startSample;
+		static_cast<FileReaderEditor*>(getEditor())->setCurrentTime(samplesToMilliseconds(currentSample));
+		break;
+	case 2: //set stop time
+		stopSample = millisecondsToSamples(newValue);
+		currentSample = startSample;
+		static_cast<FileReaderEditor*>(getEditor())->setCurrentTime(samplesToMilliseconds(currentSample));
+		break;
+	}
 }
 
+unsigned int FileReader::samplesToMilliseconds(int64 samples)
+{
+	return (unsigned int)(1000.f*float(samples)/currentSampleRate);
+}
 
+int64 FileReader::millisecondsToSamples(unsigned int ms)
+{
+	return (int64)(currentSampleRate*float(ms)/1000.f);
+}
 
 void FileReader::saveCustomParametersToXml(XmlElement* parentElement)
 {
diff --git a/Source/Processors/FileReader/FileReader.h b/Source/Processors/FileReader/FileReader.h
index 09f5a4f89..b8a74beb2 100644
--- a/Source/Processors/FileReader/FileReader.h
+++ b/Source/Processors/FileReader/FileReader.h
@@ -29,8 +29,9 @@
 #include "../../../JuceLibraryCode/JuceHeader.h"
 
 #include "../GenericProcessor/GenericProcessor.h"
+#include "FileSource.h"
 
-#define BUFFER_SIZE 102400
+#define BUFFER_SIZE 1024
 
 /**
 
@@ -73,7 +74,7 @@ public:
     int getDefaultNumOutputs();
     float getBitVolts(int chan);
 
-    void setFile(String fullpath);
+    bool setFile(String fullpath);
     String getFile();
 
     void saveCustomParametersToXml(XmlElement* parentElement);
@@ -83,16 +84,22 @@ private:
 
     int64 timestamp;
 
-    int lengthOfInputFile;
-    FILE* input;
+	float currentSampleRate;
+	int currentNumChannels;
+	int64 currentNumSamples;
+	int64 startSample;
+	int64 stopSample;
+	Array<RecordedChannelInfo> channelInfo;
 
-    int16 readBuffer[BUFFER_SIZE];
+	int64 currentSample;
 
-    int bufferSize;
+	ScopedPointer<FileSource> input;
 
-    String filePath;
+	HeapBlock<int16> readBuffer;
 
-    int counter;
+	void setActiveRecording(int index);
+	unsigned int samplesToMilliseconds(int64 samples);
+	int64 millisecondsToSamples(unsigned int ms);
 
     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FileReader);
 
diff --git a/Source/Processors/FileReader/FileReaderEditor.cpp b/Source/Processors/FileReader/FileReaderEditor.cpp
index c565aa3ec..ead262ce8 100644
--- a/Source/Processors/FileReader/FileReaderEditor.cpp
+++ b/Source/Processors/FileReader/FileReaderEditor.cpp
@@ -24,6 +24,7 @@
 #include "FileReaderEditor.h"
 
 #include "FileReader.h"
+#include "../../UI/EditorViewport.h"
 
 #include <stdio.h>
 
@@ -36,15 +37,27 @@ FileReaderEditor::FileReaderEditor(GenericProcessor* parentNode, bool useDefault
 
     lastFilePath = File::getCurrentWorkingDirectory();
 
-    fileButton = new UtilityButton("Select file",Font("Small Text", 13, Font::plain));
+    fileButton = new UtilityButton("F:",Font("Small Text", 13, Font::plain));
     fileButton->addListener(this);
-    fileButton->setBounds(30,50,120,25);
+    fileButton->setBounds(5,27,20,20);
     addAndMakeVisible(fileButton);
 
     fileNameLabel = new Label("FileNameLabel", "No file selected.");
-    fileNameLabel->setBounds(20,80,140,25);
+    fileNameLabel->setBounds(30,25,140,20);
     addAndMakeVisible(fileNameLabel);
 
+	recordSelector = new ComboBox("Recordings");
+	recordSelector->setBounds(30,50,120,20);
+	addAndMakeVisible(recordSelector);
+
+	currentTime = new DualTimeComponent(this, false);
+	currentTime->setBounds(5,80,175,20);
+	addAndMakeVisible(currentTime);
+
+	timeLimits = new DualTimeComponent(this,true);
+	timeLimits->setBounds(5,105,175,20);
+	addAndMakeVisible(timeLimits);
+
     desiredWidth = 180;
 
     setEnabledState(false);
@@ -61,11 +74,17 @@ void FileReaderEditor::setFile(String file)
 
     File fileToRead(file);
     lastFilePath = fileToRead.getParentDirectory();
-    fileReader->setFile(fileToRead.getFullPathName());
-    fileNameLabel->setText(fileToRead.getFileName(), dontSendNotification);
-
-    setEnabledState(true);
-
+    if(fileReader->setFile(fileToRead.getFullPathName()))
+	{
+		fileNameLabel->setText(fileToRead.getFileName(), dontSendNotification);
+
+		setEnabledState(true);
+	}
+	else
+	{
+		clearEditor();
+	}
+	getEditorViewport()->makeEditorVisible(this, false, true);
     repaint();
 }
 
@@ -98,6 +117,79 @@ void FileReaderEditor::buttonEvent(Button* button)
     }
 }
 
+bool FileReaderEditor::setPlaybackStartTime(unsigned int ms)
+{
+	if (ms > timeLimits->getTimeMilliseconds(1))
+		return false;
+	fileReader->setParameter(1,ms);
+	return true;
+}
+
+bool FileReaderEditor::setPlaybackStopTime(unsigned int ms)
+{
+	if (ms > recTotalTime)
+		return false;
+	fileReader->setParameter(2,ms);
+	return true;
+}
+
+void FileReaderEditor::setTotalTime(unsigned int ms)
+{
+	timeLimits->setTimeMilliseconds(0,0);
+	timeLimits->setTimeMilliseconds(1,ms);
+	currentTime->setTimeMilliseconds(0,0);
+	currentTime->setTimeMilliseconds(1,ms);
+	recTotalTime = ms;
+}
+
+void FileReaderEditor::setCurrentTime(unsigned int ms)
+{
+	MessageManagerLock mml;
+	currentTime->setTimeMilliseconds(0,ms);
+}
+
+void FileReaderEditor::comboBoxChanged(ComboBox *combo)
+{
+	fileReader->setParameter(0,combo->getSelectedId()-1);
+	getEditorViewport()->makeEditorVisible(this, false, true);
+}
+
+void FileReaderEditor::populateRecordings(FileSource* source)
+{
+	recordSelector->clear(dontSendNotification);
+	for (int i=0; i < source->getNumRecords(); i++)
+	{
+		std::cout << "adding " << source->getRecordName(1) << std::endl;
+		recordSelector->addItem(source->getRecordName(i),i+1);
+	}
+	recordSelector->setSelectedId(1,dontSendNotification);
+}
+
+void FileReaderEditor::clearEditor()
+{
+	fileNameLabel->setText("No file selected.",dontSendNotification);
+	recordSelector->clear(dontSendNotification);
+	timeLimits->setTimeMilliseconds(0,0);
+	timeLimits->setTimeMilliseconds(1,0);
+	currentTime->setTimeMilliseconds(0,0);
+	currentTime->setTimeMilliseconds(1,0);
+	setEnabledState(false);
+}
+
+void FileReaderEditor::startAcquisition()
+{
+	recordSelector->setEnabled(false);
+	timeLimits->setEnable(false);
+	GenericEditor::startAcquisition();
+}
+
+void FileReaderEditor::stopAcquisition()
+{
+	recordSelector->setEnabled(true);
+	timeLimits->setEnable(true);
+	GenericEditor::stopAcquisition();
+}
+
 void FileReaderEditor::saveEditorParameters(XmlElement* xml)
 {
 
@@ -120,4 +212,118 @@ void FileReaderEditor::loadEditorParameters(XmlElement* xml)
     //       }
     //   }
 
+}
+
+DualTimeComponent::DualTimeComponent(FileReaderEditor*e, bool isEditable)
+	: editor(e), editable(isEditable)
+{
+	Label* l;
+	l = new Label("Time1");
+	l->setBounds(0,0,75,20);
+	l->setEditable(editable);
+	l->setFont(Font("Small Text",10,Font::plain));
+	if (editable)
+	{
+		l->addListener(this);
+		l->setColour(Label::ColourIds::backgroundColourId,Colours::lightgrey);
+		l->setColour(Label::ColourIds::outlineColourId,Colours::black);
+	}
+	addAndMakeVisible(l);
+	timeLabel[0] = l;
+
+	l = new Label("Time2");
+	l->setBounds(85,0,75,20);
+	l->setEditable(editable);
+	l->setFont(Font("Small Text",10,Font::plain));
+	if (editable)
+	{
+		l->addListener(this);
+		l->setColour(Label::ColourIds::backgroundColourId,Colours::lightgrey);
+		l->setColour(Label::ColourIds::outlineColourId,Colours::black);
+	}
+	addAndMakeVisible(l);
+	timeLabel[1] = l;
+
+	setTimeMilliseconds(0,0);
+	setTimeMilliseconds(1,0);
+}
+
+DualTimeComponent::~DualTimeComponent()
+{
+}
+
+void DualTimeComponent::paint(Graphics &g)
+{
+	String sep;
+	g.setFont(Font("Small Text",10,Font::plain));
+	g.setColour(Colours::darkgrey);
+	if (editable)
+	{
+		sep = "-";
+	}
+	else
+	{
+		sep = "/";
+	}
+	g.drawText(sep,78,0,5,20,Justification::centred,false);
+}
+
+void DualTimeComponent::setTimeMilliseconds(unsigned int index, unsigned int time)
+{
+	int msFrac,secFrac,minFrac,hourFrac;
+	if (index > 1) return;
+
+	msTime[index]=time;
+
+	msFrac = time%1000;
+	time /= 1000;
+	secFrac = time%60;
+	time /= 60;
+	minFrac = time%60;
+	time /= 60;
+	hourFrac = time;
+
+	String text = String(hourFrac).paddedLeft('0',2) + ":" + String(minFrac).paddedLeft('0',2) + ":" +
+		String(secFrac).paddedLeft('0',2) + "." + String(msFrac).paddedLeft('0',3);
+
+	timeLabel[index]->setText(text,dontSendNotification);
+}
+
+unsigned int DualTimeComponent::getTimeMilliseconds(unsigned int index)
+{
+	if (index > 1) return 0;
+	return msTime[index];
+}
+
+void DualTimeComponent::setEnable(bool enable)
+{
+	timeLabel[0]->setEnabled(enable);
+	timeLabel[1]->setEnabled(enable);
+}
+
+void DualTimeComponent::labelTextChanged(Label* label)
+{
+    StringArray elements;
+    unsigned int time;
+    bool res;
+    int index = (label == timeLabel[0]) ? 0 : 1;
+
+    elements.addTokens(label->getText(),":.",String::empty);
+    time = elements[0].getIntValue();
+    time = 60*time + elements[1].getIntValue();
+    time = 60*time + elements[2].getIntValue();
+    time = 1000*time + elements[3].getIntValue();
+
+    if (index == 0)
+        res = editor->setPlaybackStartTime(time);
+    else
+        res = editor->setPlaybackStopTime(time);
+    if (res)
+    {
+        setTimeMilliseconds(index,time);
+    }
+    else
+    {
+        setTimeMilliseconds(index,getTimeMilliseconds(index));
+    }
 }
\ No newline at end of file
diff --git a/Source/Processors/FileReader/FileReaderEditor.h b/Source/Processors/FileReader/FileReaderEditor.h
index d359796c7..0f7529129 100644
--- a/Source/Processors/FileReader/FileReaderEditor.h
+++ b/Source/Processors/FileReader/FileReaderEditor.h
@@ -29,7 +29,8 @@
 #include "../Editors/GenericEditor.h"
 
 class FileReader;
-
+class DualTimeComponent;
+class FileSource;
 
 /**
 
@@ -39,7 +40,8 @@ class FileReader;
 
 */
 
-class FileReaderEditor : public GenericEditor
+class FileReaderEditor : public GenericEditor,
+	public ComboBox::Listener
 
 {
 public:
@@ -54,19 +56,56 @@ public:
 
     void loadEditorParameters(XmlElement*);
 
+	bool setPlaybackStartTime(unsigned int ms);
+	bool setPlaybackStopTime(unsigned int ms);
+	void setTotalTime(unsigned int ms);
+	void setCurrentTime(unsigned int ms);
+
+	void comboBoxChanged(ComboBox *combo);
+	void populateRecordings(FileSource* source);
+
+	void startAcquisition();
+	void stopAcquisition();
+
 private:
 
     ScopedPointer<UtilityButton> fileButton;
     ScopedPointer<Label> fileNameLabel;
+	ScopedPointer<ComboBox> recordSelector;
+	ScopedPointer<DualTimeComponent> currentTime;
+	ScopedPointer<DualTimeComponent> timeLimits;
 
     FileReader* fileReader;
+	unsigned int recTotalTime;
 
     File lastFilePath;
 
+	void clearEditor();
+
     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FileReaderEditor);
 
 };
 
+class DualTimeComponent : public Component,
+	public Label::Listener
+{
+public:
+	DualTimeComponent(FileReaderEditor* e, bool isEditable);
+	~DualTimeComponent();
+	void setTimeMilliseconds(unsigned int index, unsigned int time);
+	unsigned int getTimeMilliseconds(unsigned int index);
+	void paint(Graphics& g);
+	void labelTextChanged(Label* label);
+	void setEnable(bool enable);
+
+private:
+	ScopedPointer<Label> timeLabel[2];
+	unsigned int msTime[2];
+	FileReaderEditor* editor;
+	bool editable;
+
+};
+
 
 
 #endif  // __FILEREADEREDITOR_H_D6EC8B48__
diff --git a/Source/Processors/FileReader/FileSource.cpp b/Source/Processors/FileReader/FileSource.cpp
index ce721a6ee..a30b3ddab 100644
--- a/Source/Processors/FileReader/FileSource.cpp
+++ b/Source/Processors/FileReader/FileSource.cpp
@@ -38,7 +38,7 @@ int FileSource::getNumRecords()
 
 String FileSource::getRecordName(int index)
 {
-	return infoArray[activeRecord].name;
+	return infoArray[index].name;
 }
 
 int FileSource::getRecordNumChannels(int index)
@@ -98,16 +98,23 @@ bool FileSource::fileIsOpened()
 	return fileOpened;
 }
 
+String FileSource::getFileName()
+{
+	return filename;
+}
+
 bool FileSource::OpenFile(File file)
 {
 	if (Open(file))
 	{
 		fileOpened = true;
 		fillRecordInfo();
+		filename = file.getFullPathName();
 	}
 	else
 	{
 		fileOpened = false;
+		filename = String::empty;
 	}
 	return fileOpened;
 }
\ No newline at end of file
diff --git a/Source/Processors/FileReader/FileSource.h b/Source/Processors/FileReader/FileSource.h
index 1e3e5e7a1..2d2388c53 100644
--- a/Source/Processors/FileReader/FileSource.h
+++ b/Source/Processors/FileReader/FileSource.h
@@ -57,9 +57,10 @@ public:
 
     bool OpenFile(File file);
     bool fileIsOpened();
+	String getFileName();
 
     virtual int readData(int16* buffer, int nSamples) =0;
-
+	virtual void processChannelData(int16* inBuffer, float* outBuffer, int channel, int64 numSamples)=0;
     virtual void seekTo(int64 sample) =0;
 
 protected:
@@ -75,6 +76,7 @@ protected:
     bool fileOpened;
     int numRecords;
     int activeRecord;
+	String filename;
 
 private:
     virtual bool Open(File file)=0;
diff --git a/Source/Processors/FileReader/KwikFileSource.cpp b/Source/Processors/FileReader/KwikFileSource.cpp
index 71231e514..3249b5487 100644
--- a/Source/Processors/FileReader/KwikFileSource.cpp
+++ b/Source/Processors/FileReader/KwikFileSource.cpp
@@ -215,4 +215,16 @@ int KWIKFileSource::readData(int16* buffer, int nSamples)
 		return 0;
 	}
 	return 0;
+}
+
+void KWIKFileSource::processChannelData(int16* inBuffer, float* outBuffer, int channel, int64 numSamples)
+{
+	int n = getActiveNumChannels();
+	float bitVolts = getChannelInfo(channel).bitVolts;
+
+	for(int i=0; i < numSamples; i++)
+	{
+		*(outBuffer+i) = *(inBuffer+(n*i)+channel) * bitVolts;
+	}
+
 }
\ No newline at end of file
diff --git a/Source/Processors/FileReader/KwikFileSource.h b/Source/Processors/FileReader/KwikFileSource.h
index 8cf717f9a..49af9d4b1 100644
--- a/Source/Processors/FileReader/KwikFileSource.h
+++ b/Source/Processors/FileReader/KwikFileSource.h
@@ -48,6 +48,8 @@ public:
 
 	void seekTo(int64 sample);
 
+	void processChannelData(int16* inBuffer, float* outBuffer, int channel, int64 numSamples);
+
 private:
 	ScopedPointer<H5::H5File> sourceFile;
 	ScopedPointer<H5::DataSet> dataSet;
-- 
GitLab