/* ------------------------------------------------------------------ This file is part of the Open Ephys GUI Copyright (C) 2013 Open Ephys ------------------------------------------------------------------ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "FileReader.h" #include "FileReaderEditor.h" #include <stdio.h> #include "../../AccessClass.h" #include "../PluginManager/PluginManager.h" FileReader::FileReader() : GenericProcessor ("File Reader") , timestamp (0) , currentSampleRate (0) , currentNumChannels (0) , currentSample (0) , currentNumSamples (0) , startSample (0) , stopSample (0) , counter (0) { enabledState (false); const int numFileSources = AccessClass::getPluginManager()->getNumFileSources(); for (int i = 0; i < numFileSources; ++i) { Plugin::FileSourceInfo info = AccessClass::getPluginManager()->getFileSourceInfo (i); StringArray extensions; extensions.addTokens (info.extensions, ";", "\""); const int numExtensions = extensions.size(); for (int j = 0; j < numExtensions; ++j) { supportedExtensions.set (extensions[j].toLowerCase(), i + 1); } } } FileReader::~FileReader() { } AudioProcessorEditor* FileReader::createEditor() { editor = new FileReaderEditor (this, true); return editor; } bool FileReader::isReady() /* const */ { if (! input) { CoreServices::sendStatusMessage ("No file selected in File Reader."); return false; } else { return input->isReady(); } } float FileReader::getDefaultSampleRate() /* const */ { if (input) return currentSampleRate; else return 44100.0; } int FileReader::getNumHeadstageOutputs() /* const */ { if (input) return currentNumChannels; else return 16; } int FileReader::getNumEventChannels() /* const */ { return 8; } float FileReader::getBitVolts (Channel* chan) /* const */ { if (input) return chan->bitVolts; else return 0.05f; } void FileReader::enabledState (bool t) { isEnabled = t; } bool FileReader::isFileSupported (const String& fileName) const { const File file (fileName); String ext = file.getFileExtension().toLowerCase().substring (1); return isFileExtensionSupported (ext); } bool FileReader::isFileExtensionSupported (const String& ext) const { const int index = supportedExtensions[ext] - 1; const bool isExtensionSupported = index >= 0; return isExtensionSupported; } bool FileReader::setFile (String fullpath) { File file (fullpath); String ext = file.getFileExtension().toLowerCase().substring (1); const int index = supportedExtensions[ext] - 1; const bool isExtensionSupported = index >= 0; if (isExtensionSupported) { const int index = supportedExtensions[ext] - 1; Plugin::FileSourceInfo sourceInfo = AccessClass::getPluginManager()->getFileSourceInfo (index); input = sourceInfo.creator(); } else { CoreServices::sendStatusMessage ("File type not supported"); return false; } if (! input->OpenFile (file)) { input = nullptr; CoreServices::sendStatusMessage ("Invalid file"); return false; } const bool isEmptyFile = input->getNumRecords() <= 0; if (isEmptyFile) { input = nullptr; CoreServices::sendStatusMessage ("Empty file. Inoring open operation"); return false; } static_cast<FileReaderEditor*> (getEditor())->populateRecordings (input); setActiveRecording (0); return true; } void FileReader::setActiveRecording (int index) { input->setActiveRecord (index); currentNumChannels = input->getActiveNumChannels(); currentNumSamples = input->getActiveNumSamples(); currentSampleRate = input->getActiveSampleRate(); currentSample = 0; startSample = 0; stopSample = currentNumSamples; 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() const { 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; // } } void FileReader::process (AudioSampleBuffer& buffer, MidiBuffer& events) { setTimestamp (events, timestamp); const int samplesNeeded = int (float (buffer.getNumSamples()) * (getDefaultSampleRate() / 44100.0f)); // FIXME: needs to account for the fact that the ratio might not be an exact // integer value int samplesRead = 0; while (samplesRead < samplesNeeded) { int samplesToRead = samplesNeeded - samplesRead; if ( (currentSample + samplesToRead) > stopSample) { samplesToRead = stopSample - currentSample; if (samplesToRead > 0) input->readData (readBuffer + samplesRead, samplesToRead); input->seekTo (startSample); currentSample = startSample; } else { input->readData (readBuffer + samplesRead, samplesToRead); currentSample += samplesToRead; } samplesRead += samplesToRead; } for (int i = 0; i < currentNumChannels; ++i) { input->processChannelData (readBuffer, buffer.getWritePointer (i, 0), i, samplesNeeded); } timestamp += samplesNeeded; setNumSamples (events, samplesNeeded); // code for testing events: // // =========================================================================== // if (counter == 100) // { // //std::cout << "Adding on event for node id: " << nodeId << std::endl; // addEvent (events, // MidiBuffer // TTL, // eventType // 0, // sampleNum // 1, // eventID // 0); // eventChannel // ++counter; // } // else if (counter > 120) // { // //std::cout << "Adding off event!" << std::endl; // addEvent (events, // MidiBuffer // TTL, // eventType // 0, // sampleNum // 0, // eventID // 0); // eventChannel // counter = 0; // } // else // { // ++counter; // } // // =========================================================================== } void FileReader::setParameter (int parameterIndex, float newValue) { switch (parameterIndex) { //Change selected recording case 0: setActiveRecording (newValue); break; //set startTime case 1: startSample = millisecondsToSamples (newValue); currentSample = startSample; static_cast<FileReaderEditor*> (getEditor())->setCurrentTime (samplesToMilliseconds (currentSample)); break; //set stop time case 2: stopSample = millisecondsToSamples(newValue); currentSample = startSample; static_cast<FileReaderEditor*> (getEditor())->setCurrentTime (samplesToMilliseconds (currentSample)); break; } } unsigned int FileReader::samplesToMilliseconds (int64 samples) const { return (unsigned int) (1000.f * float (samples) / currentSampleRate); } int64 FileReader::millisecondsToSamples (unsigned int ms) const { return (int64) (currentSampleRate * float (ms) / 1000.f); }