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