void WiFiOutput::handleEvent(int eventType, MidiMessage& event, int sampleNum) { if (eventType == TTL) { startTimer((int) float(event.getTimeStamp())/getSampleRate()*1000.0); } }
//============================================================================== void MiosTerminal::handleIncomingMidiMessage(const MidiMessage& message, uint8 runningStatus) { uint8 *data = (uint8 *)message.getRawData(); uint32 size = message.getRawDataSize(); int messageOffset = 0; bool messageReceived = false; if( runningStatus == 0xf0 && SysexHelper::isValidMios32DebugMessage(data, size, -1) && (data[7] == 0x40 || data[7] == 0x00) ) { // allow 0x40 (received) and 0x00 (sent) terminal message // 0x00 is allowed for the "feedback test" which is described in the MIDI troubleshooting guide messageOffset = 8; messageReceived = true; } if( messageReceived ) { String str = ""; for(int i=messageOffset; i<size; ++i) { if( data[i] < 0x80 ) { if( data[i] != '\n' || size < (i+1) ) str += String::formatted(T("%c"), data[i] & 0x7f); } } if( !gotFirstMessage ) terminalLogBox->clear(); gotFirstMessage = 1; double timeStamp = message.getTimeStamp() ? message.getTimeStamp() : ((double)Time::getMillisecondCounter() / 1000.0); String timeStampStr = (timeStamp > 0) ? String::formatted(T("%8.3f"), timeStamp) : T("now"); String terminalStr = "[" + timeStampStr + "] " + str; if( miosStudio->runningInBatchMode() ) { std::cout << terminalStr << std::endl; } else { terminalLogBox->addEntry(Colours::black, terminalStr); } } }
void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) { #if JUCE_DEBUG jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object #endif // the messages that come in here need to be time-stamped correctly - see MidiInput // for details of what the number should be. jassert (message.getTimeStamp() != 0); const ScopedLock sl (midiCallbackLock); auto sampleNumber = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate); incomingMessages.addEvent (message, sampleNumber); // if the messages don't get used for over a second, we'd better // get rid of any old ones to avoid the queue getting too big if (sampleNumber > sampleRate) incomingMessages.clear (0, sampleNumber - (int) sampleRate); }
//============================================================================== void MidiSequencePlugin::getControllerIndexed (const int index, int& controllerNum, double& value, double& beat) { int numNoteOn = 0; for (int i = 0; i < midiSequence->getNumEvents (); i++) { MidiMessageSequence::MidiEventHolder* eventOn = midiSequence->getEventPointer (i); MidiMessage* msgOn = & eventOn->message; if (eventOn->message.isController ()) { if (index == numNoteOn) { controllerNum = msgOn->getControllerNumber(); value = msgOn->getControllerValue () / 127.0; beat = msgOn->getTimeStamp (); break; } numNoteOn++; } } }
void CtrlrPanelProcessor::processBlock(MidiBuffer &midiMessages, MidiBuffer &leftoverBuffer) { if (owner.getMidiOptionBool(panelMidiInputFromHostCompare)) { owner.getMIDIInputThread().handleMIDIFromHost(midiMessages); } MidiBuffer::Iterator i(midiMessages); MidiMessage m; int time; while (i.getNextEvent(m,time)) { _MIN("VST INPUT", m); if (owner.getMidiOptionBool(panelMidiThruH2D) == true) { if (owner.getMidiOptionBool(panelMidiThruH2DChannelize)) { m.setChannel (owner.getMidiChannel(panelMidiOutputChannelDevice)); } owner.sendMidi(m); } if (owner.getMidiOptionBool(panelMidiThruH2H) == true) { if (owner.getMidiOptionBool(panelMidiThruH2HChannelize)) { m.setChannel (owner.getMidiChannel(panelMidiOutputChannelHost)); } leftoverBuffer.addEvent (m, m.getTimeStamp()); } } }
//============================================================================== void MidiSequencePlugin::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { MidiSequencePluginBase::processBlock(buffer, midiMessages); MidiMessageSequence sourceMidi = *midiSequence; std::vector<int> doneTheseControllers; if (transport->isPlaying () && getBoolValue(PROP_SEQENABLED, true)) { const int blockSize = buffer.getNumSamples (); MidiBuffer* midiBuffer = midiBuffers.getUnchecked (0); const int frameCounter = transport->getPositionInFrames (); const int framesPerBeat = transport->getFramesPerBeat (); const int nextBlockFrameNumber = frameCounter + blockSize; const int seqIndex = getLoopRepeatIndex(); const double beatCount = getLoopBeatPosition(); const double frameLenBeatCount = (nextBlockFrameNumber - frameCounter) / (double)framesPerBeat; double frameEndBeatCount = beatCount + frameLenBeatCount; if (frameEndBeatCount > getLengthInBeats()) frameEndBeatCount -= getLengthInBeats(); // loop for each controller we need to interpolate MidiMessage* lastCtrlEvent = NULL; do { lastCtrlEvent = NULL; // hunt for a controller event before now int i; for (i = 0; i < sourceMidi.getNumEvents (); i++) { int timeStampInSeq = roundFloatToInt (sourceMidi.getEventTime (i) * framesPerBeat); int timeStamp = timeStampInSeq + (seqIndex * getLengthInBeats() * framesPerBeat); MidiMessage* midiMessage = &sourceMidi.getEventPointer (i)->message; if (timeStamp >= nextBlockFrameNumber || !midiMessage) break; // event is after now, leave //if (midiMessage->isController() && (std::find(doneTheseControllers.begin(), doneTheseControllers.end(), midiMessage->getControllerNumber()) == doneTheseControllers.end())) // lastCtrlEvent = midiMessage; } // hunt for a matching event after that one if (lastCtrlEvent) { // store the controller number so we know which controllers we've done doneTheseControllers.push_back(lastCtrlEvent->getControllerNumber()); MidiMessage* nextCtrlEvent = NULL; for (; i < sourceMidi.getNumEvents (); i++) { MidiMessage* midiMessage = &sourceMidi.getEventPointer (i)->message; if (midiMessage->isController() && midiMessage->getControllerNumber() == lastCtrlEvent->getControllerNumber()) { nextCtrlEvent = midiMessage; break; } } // render an interpolated event!... if (nextCtrlEvent) { double bt = nextCtrlEvent->getTimeStamp(); double at = lastCtrlEvent->getTimeStamp(); double deltaBeats = bt - at; int a = lastCtrlEvent->getControllerValue(); int b = nextCtrlEvent->getControllerValue(); double now = beatCount + (frameEndBeatCount - beatCount) / 2.0; double interpRemainBeats = deltaBeats - (now - at); if (deltaBeats > 0) { double nextPart = interpRemainBeats / deltaBeats; nextPart = 1 - nextPart; double interpdVal = a + nextPart * (b - a); MidiMessage interpy = MidiMessage::controllerEvent(lastCtrlEvent->getChannel(), lastCtrlEvent->getControllerNumber(), static_cast<int>(interpdVal)); midiBuffer->addEvent (interpy, (nextBlockFrameNumber - frameCounter) / 2); } else { DBG ("Negative delta beats when rendering automation!!"); } } } // now we also need to do that again if there are multiple events per frame AND we are interpolating multiple times per frame // (at the moment only interpolating once per audio frame) } while (lastCtrlEvent != NULL); } }
virtual void menuItemSelected(int menuItemID, int) { if (menuItemID == 200) { WildcardFileFilter wildcardFilter("*.mid", String::empty, "Midi files"); FileBrowserComponent browser(FileBrowserComponent::canSelectFiles | FileBrowserComponent::openMode, lastOpenedFile.exists() ? lastOpenedFile : File(String("C:\\Users\\GeorgeKrueger\\Documents")), &wildcardFilter, nullptr); FileChooserDialogBox dialogBox("Open a midi file", "Please choose a midi file to open...", browser, false, Colours::lightgrey); if (dialogBox.show()) { File selectedFile = browser.getSelectedFile(0); lastOpenedFile = selectedFile; FileInputStream fileStream(selectedFile); juce::MidiFile midiFile; midiFile.readFrom(fileStream); int numTracks = midiFile.getNumTracks(); midiFile.convertTimestampTicksToSeconds(); String msg; msg << "Opened midi file: " << selectedFile.getFileName() << " Tracks: " << numTracks << "\n"; log(msg); for (int i = 0; i < numTracks; ++i) { const MidiMessageSequence* msgSeq = midiFile.getTrack(i); OwnedArray<PluginDescription> results; String plugFile = "C:\\VST\\FMMF.dll"; VSTPluginFormat vstFormat; vstFormat.findAllTypesForFile(results, plugFile); if (results.size() > 0) { msg.clear(); msg << "Found " << results.size() << " plugin(s) matching file " << plugFile << "\n"; log(msg); int secsToRender = 10; double sampleRate = 44100; int totalSizeInSamples = static_cast<int>(44100 * secsToRender); AudioPluginInstance* plugInst = vstFormat.createInstanceFromDescription(*results[0], sampleRate, totalSizeInSamples); if (!plugInst) { msg.clear(); msg << "Failed to load plugin " << plugFile << "\n"; log(msg); continue; } int numInputChannels = plugInst->getTotalNumInputChannels(); int numOutputChannels = plugInst->getTotalNumOutputChannels(); msg.clear(); msg << "Plugin input channels: " << numInputChannels << " output channels: " << numOutputChannels << " Current program: " << plugInst->getCurrentProgram() << "\n"; log(msg); int maxChannels = std::max(numInputChannels, numOutputChannels); AudioBuffer<float> buffer(maxChannels, totalSizeInSamples); MidiBuffer midiMessages; for (int j = 0; j < msgSeq->getNumEvents(); ++j) { MidiMessageSequence::MidiEventHolder* midiEventHolder = msgSeq->getEventPointer(j); MidiMessage midiMsg = midiEventHolder->message; int samplePos = static_cast<int>(midiMsg.getTimeStamp() * sampleRate); midiMessages.addEvent(midiMsg, samplePos); } plugInst->prepareToPlay(sampleRate, totalSizeInSamples); plugInst->processBlock(buffer, midiMessages); /*File txtOutFile("C:\\Users\\GeorgeKrueger\\Documents\\GitHub\\soundserver2\\out.txt"); FileOutputStream* txtOutStream = txtOutFile.createOutputStream(); for (int j = 0; j < 44100; ++j) { float sample = buffer.getSample(0, j); txtOutStream->writeFloat(sample); txtOutStream->writeText(" ", true, false); }*/ File outputFile("C:\\Users\\GeorgeKrueger\\Documents\\GitHub\\soundserver2\\out.wav"); if (outputFile.exists()) { outputFile.deleteFile(); } FileOutputStream* fileOutputStream = outputFile.createOutputStream(); WavAudioFormat wavFormat; StringPairArray metadataValues; juce::AudioFormatWriter* wavFormatWriter = wavFormat.createWriterFor( fileOutputStream, sampleRate, 2, 16, metadataValues, 0); bool writeAudioDataRet = wavFormatWriter->writeFromAudioSampleBuffer(buffer, 0, buffer.getNumSamples()); wavFormatWriter->flush(); msg.clear(); msg << "Done writing to output file " << outputFile.getFileName() << " . Write return value: " << (int)writeAudioDataRet << "\n"; log(msg); delete wavFormatWriter; delete plugInst; } else { msg.clear(); msg << "Could not find plugin from file " << plugFile << "\n"; log(msg); } } } } }
void LfpDisplayNode::handleEvent(int eventType, MidiMessage& event, int sampleNum) { if (eventType == TTL) { const uint8* dataptr = event.getRawData(); //int eventNodeId = *(dataptr+1); int eventId = *(dataptr+2); int eventChannel = *(dataptr+3); int eventTime = event.getTimeStamp(); int eventSourceNodeId = *(dataptr+5); int nSamples = numSamples.at(eventSourceNodeId); int samplesToFill = nSamples - eventTime; // std::cout << "Received event from " << eventSourceNode << ", channel " // << eventChannel << ", with ID " << eventId << ", copying to " // << channelForEventSource[eventSourceNode] << std::endl; //// int bufferIndex = (displayBufferIndex[channelForEventSource[eventSourceNodeId]] + eventTime - nSamples) % displayBuffer->getNumSamples(); if (eventId == 1) { ttlState[eventSourceNodeId] |= (1L << eventChannel); } else { ttlState[eventSourceNodeId] &= ~(1L << eventChannel); } if (samplesToFill + bufferIndex < displayBuffer->getNumSamples()) { //std::cout << bufferIndex << " " << samplesToFill << " " << ttlState[eventSourceNode] << std::endl; displayBuffer->copyFrom(channelForEventSource[eventSourceNodeId], // destChannel bufferIndex, // destStartSample arrayOfOnes, // source samplesToFill, // numSamples float(ttlState[eventSourceNodeId])); // gain } else { int block2Size = (samplesToFill + bufferIndex) % displayBuffer->getNumSamples(); int block1Size = samplesToFill - block2Size; //std::cout << "OVERFLOW." << std::endl; //std::cout << bufferIndex << " " << block1Size << " " << ttlState << std::endl; displayBuffer->copyFrom(channelForEventSource[eventSourceNodeId], // destChannel bufferIndex, // destStartSample arrayOfOnes, // source block1Size, // numSamples float(ttlState[eventSourceNodeId])); // gain //std::cout << 0 << " " << block2Size << " " << ttlState << std::endl; displayBuffer->copyFrom(channelForEventSource[eventSourceNodeId], // destChannel 0, // destStartSample arrayOfOnes, // source block2Size, // numSamples float(ttlState[eventSourceNodeId])); // gain } // std::cout << "ttlState: " << ttlState << std::endl; // std::cout << "Received event from " << eventNodeId << // " on channel " << eventChannel << // " with value " << eventId << // " at timestamp " << event.getTimeStamp() << std::endl; } }
int main(int argc, char **argv) { if (argc < 2) { printf("Usage %s file.mid\n", argv[0]); return(-1); } const char *l_pszFileName = argv[1]; File file(l_pszFileName); const double mm_per_second = 1000.0 / 60.0; // How fast does the tape move through the music box? (1 meter per minute - by observation) const double distance_between_notes_in_mm = 2.0; // across the paper. const double min_distance_between_notes = 7.0; // Cannot have two consecutive notes appear less than this distance between each other. std::list<std::string> gcode; std::list<std::string> heeks; int id=1; typedef std::vector<std::string> Keys_t; Keys_t keys, legal_keys; Keys_t::size_type middle_c_key; // Which integer tells us it's the 'C' in the middle or the 'C' in the // octave above or below. const int middle_c_octave = 5; for (int octave=2; octave <= 8; octave++) { for (char key='A'; key<='G'; key++) { std::ostringstream l_ossKey; // l_ossKey << key << middle_c_octave - 0; l_ossKey << key << octave; keys.push_back( l_ossKey.str() ); if ((key == 'C') && (octave == middle_c_octave)) middle_c_key = keys.size()-1; } } // Setup our scale of notes that will work with the music box. It covers from 'C' to 'C' over two octaves. // Octave below middle C for (char key='C'; key<='G'; key++) { std::ostringstream l_ossKey; l_ossKey << key << middle_c_octave - 1; legal_keys.push_back( l_ossKey.str() ); } // Octave that includes middle C for (char key='A'; key<='G'; key++) { std::ostringstream l_ossKey; l_ossKey << key << middle_c_octave - 0; legal_keys.push_back( l_ossKey.str() ); } // Octave above middle C for (char key='A'; key<='C'; key++) { std::ostringstream l_ossKey; l_ossKey << key << middle_c_octave + 1; legal_keys.push_back( l_ossKey.str() ); } const double track_width = distance_between_notes_in_mm * keys.size(); const double space_between_tracks = track_width * 0.75; MidiFile midi_file; FileInputStream midi_input_stream(file); if (! midi_file.readFrom( midi_input_stream )) { fprintf(stderr,"Could not open '%s' for reading\n", l_pszFileName); return(-1); } midi_file.convertTimestampTicksToSeconds(); std::set<int> notes; double time_scale = 1.0; bool time_scale_changed = false; do { std::map<std::string, double> key_position; time_scale_changed = false; gcode.clear(); heeks.clear(); key_position.clear(); std::ostringstream l_ossGCode; for (int track = 0; track<midi_file.getNumTracks(); track++) { int number_of_notes_included = 0; int number_of_notes_ignored = 0; const MidiMessageSequence *pMessageSequence = midi_file.getTrack(track); double start_time = pMessageSequence->getStartTime(); double end_time = pMessageSequence->getEndTime(); double duration = end_time - start_time; if (duration <= 0.0001) continue; l_ossGCode.str(""); l_ossGCode << "(Duration of track " << track << " is " << duration << " seconds)"; gcode.push_back( l_ossGCode.str() ); printf("%s\n", l_ossGCode.str().c_str()); // printf("Duration of track %d is %lf seconds\n", track, duration); for (int event = 0; event < pMessageSequence->getNumEvents(); event++) { MidiMessageSequence::MidiEventHolder *pEvent = pMessageSequence->getEventPointer(event); MidiMessage message = pEvent->message; double time_stamp = message.getTimeStamp(); if (message.isTextMetaEvent()) { String text = message.getTextFromTextMetaEvent(); char buf[1024]; memset( buf, '\0', sizeof(buf) ); text.copyToBuffer( buf, sizeof(buf)-1 ); // printf("Track %d is %s\n", track, buf ); l_ossGCode.str(""); l_ossGCode << "(Text track " << track << " is " << buf << ")"; gcode.push_back(l_ossGCode.str()); printf("%s\n", l_ossGCode.str().c_str()); std::ostringstream l_ossHeeks; l_ossHeeks << "<Text text=\"" << buf << "\" font=\"OpenGL\" col=\"0\" m0=\"-0.0443342566\" m1=\"-0.999016753\" m2=\"0\" m3=\"" << (double) (time_stamp * mm_per_second) << "\" m4=\"0.999016753\" m5=\"-0.0443342566\" m6=\"0\" m7=\"" << (double) ((track_width + space_between_tracks) * track) << "\" m8=\"0\" m9=\"0\" ma=\"1\" mb=\"0\" id=\"" << id++ << "\" />"; heeks.push_back( l_ossHeeks.str() ); } if (message.isTrackNameEvent()) { String text = message.getTextFromTextMetaEvent(); char buf[1024]; memset( buf, '\0', sizeof(buf) ); text.copyToBuffer( buf, sizeof(buf)-1 ); printf("Track %d is %s\n", track, buf ); } if (message.isNoteOn()) { char note_name[256]; memset( note_name, '\0', sizeof(note_name) ); message.getMidiNoteName(message.getNoteNumber(), true, true, middle_c_octave).copyToBuffer( note_name, sizeof(note_name)-1 ); notes.insert( message.getNoteNumber() ); // printf("time %lf note %s\n", time_stamp, note_name ); std::string l_ssNoteName(note_name); std::string::size_type offset; bool sharp_found = false; while ((offset = l_ssNoteName.find("#")) != std::string::npos) { l_ssNoteName = l_ssNoteName.erase(offset,1); sharp_found = true; } strncpy( note_name, l_ssNoteName.c_str(), sizeof(note_name)-1 ); const int blue = 16711680; const int black = 0; const int red = 255; int colour = blue; Keys_t::iterator l_itLegalKey = std::find( legal_keys.begin(), legal_keys.end(), note_name ); if (l_itLegalKey == legal_keys.end()) { colour = red; } // Find the note name in the keys we're interested in. Keys_t::iterator l_itKey = std::find( keys.begin(), keys.end(), note_name ); if (l_itKey != keys.end()) { double x = time_stamp * mm_per_second * time_scale; double y = double(double(std::distance( keys.begin(), l_itKey )) - double(middle_c_key)) * distance_between_notes_in_mm; y += ((track_width + space_between_tracks) * track); if (sharp_found) { y += (distance_between_notes_in_mm / 2.0); colour = red; } // Check to see if we have two notes that are too close to each other for the mechanism to play them. if (key_position.find(note_name) == key_position.end()) { key_position[note_name] = x; } // Measure the distance between this note and the previous equivalent note. If we need to expand our // time scale to ensure consecutive notes are not too close together, do it now. if ((dist(x, key_position[note_name]) < min_distance_between_notes) && (dist(x, key_position[note_name]) > 0.0)) { // Need to scale the whole piece up. double increase_in_time_scale = double(double(min_distance_between_notes) / double(dist(x, key_position[note_name]))); if (increase_in_time_scale > 1.0) { time_scale = increase_in_time_scale * time_scale; time_scale_changed = true; } } key_position[note_name] = x; // It's a key we have to play. Generate the GCode. l_ossGCode.str(""); l_ossGCode << "G83 X " << x << " Y " << y << "\t(" << note_name << ")"; gcode.push_back( l_ossGCode.str() ); if (sharp_found) { std::ostringstream l_ossHeeks; l_ossHeeks << "<Circle col=\"" << colour << "\" r=\"" << (distance_between_notes_in_mm / 2.0) * 0.85 << "\" cx=\"" << x << "\" cy=\"" << y << "\" cz=\"0\" ax=\"0\" ay=\"0\" az=\"1\" id=\"" << id++ << "\">\n"; l_ossHeeks << " <Point col=\"" << colour << "\" x=\"" << x << "\" y=\"" << y << "\" z=\"0\" id=\"" << id++ << "\" />\n"; l_ossHeeks << "</Circle>\n"; heeks.push_back( l_ossHeeks.str() ); } else { std::ostringstream l_ossHeeks; l_ossHeeks << "<Point col=\"" << colour << "\" x=\"" << x << "\" y=\"" << y << "\" z=\"0\" id=\"" << id++ << "\" />"; heeks.push_back( l_ossHeeks.str() ); } // printf("G83 Want hole for key %s at %lf,%lf\n", note_name, x, y ); number_of_notes_included++; } else { // This key doesn't fall exactly on our scale. Ignore it. number_of_notes_ignored++; printf("Missed note %s\n", note_name); } } // End if - then } // End for l_ossGCode.str(""); l_ossGCode << "(" << (double(number_of_notes_included)/double(number_of_notes_included + number_of_notes_ignored)) * 100.0 << " % utilisation of notes)"; gcode.push_back(l_ossGCode.str()); printf("%s\n", l_ossGCode.str().c_str()); l_ossGCode.str(""); l_ossGCode << "(Of the " << (number_of_notes_included + number_of_notes_ignored) << " notes, we are using " << number_of_notes_included << " (whole notes) and ignoring " << number_of_notes_ignored << " (sharps and flats))"; gcode.push_back(l_ossGCode.str()); printf("%s\n", l_ossGCode.str().c_str()); printf("At %lf mm per second (%lf mm per minute), we will need %lf mm of paper for this tune\n", mm_per_second, mm_per_second * 60.0 * time_scale, (end_time - start_time) * mm_per_second * time_scale ); printf("We have had to scale the tune %lf times to ensure no two consecutive notes were less than %lf mm apart\n", time_scale, min_distance_between_notes); // Draw a line for each possible note. for (Keys_t::iterator l_itKey = keys.begin(); l_itKey != keys.end(); l_itKey++) { double y = double(double(std::distance( keys.begin(), l_itKey )) - double(middle_c_key)) * distance_between_notes_in_mm; y += ((track_width + space_between_tracks) * track); if (std::find(legal_keys.begin(), legal_keys.end(), *l_itKey) != legal_keys.end()) { std::ostringstream l_ossHeeks; l_ossHeeks.str(""); l_ossHeeks << "<Sketch title=\"Sketch\" id=\"" << id++ << "\">\n"; l_ossHeeks << "<Line col=\"0\" id=\"" << id++ << "\">\n"; l_ossHeeks << "<Point col=\"0\" x=\"" << (double) (start_time * mm_per_second * time_scale) << "\" y=\"" << y << "\" z=\"0\" id=\"" << id++ << "\" />\n"; l_ossHeeks << "<Point col=\"0\" x=\"" << (double) (end_time * mm_per_second * time_scale) << "\" y=\"" << y << "\" z=\"0\" id=\"" << id++ << "\" />\n"; l_ossHeeks << "</Line>\n"; l_ossHeeks << "</Sketch>\n"; heeks.push_back(l_ossHeeks.str()); } } // End for } } while (time_scale_changed == true); /* for (std::set<int>::const_iterator l_itNote = notes.begin(); l_itNote != notes.end(); l_itNote++) { char note_name[256]; memset( note_name, '\0', sizeof(note_name) ); MidiMessage::getMidiNoteName(*l_itNote, true, true, 5).copyToBuffer( note_name, sizeof(note_name)-1 ); printf("Note %d %s\n", *l_itNote, note_name); } */ { String gcode_file_name(l_pszFileName); gcode_file_name = gcode_file_name.dropLastCharacters(4); gcode_file_name << ".ngc"; char buf[1024]; memset( buf, '\0', sizeof(buf) ); gcode_file_name.copyToBuffer(buf,sizeof(buf)-1); FILE *fp = fopen( buf, "w+t"); if (fp == NULL) { fprintf(stderr,"Could not open %s for writing\n", buf); return(-1); } for (std::list<std::string>::const_iterator l_itLine = gcode.begin(); l_itLine != gcode.end(); l_itLine++) { fprintf(fp,"%s\n", l_itLine->c_str()); } fclose(fp); } { String gcode_file_name(l_pszFileName); gcode_file_name = gcode_file_name.dropLastCharacters(4); gcode_file_name << ".heeks"; char buf[1024]; memset( buf, '\0', sizeof(buf) ); gcode_file_name.copyToBuffer(buf,sizeof(buf)-1); FILE *fp = fopen( buf, "w+t"); if (fp == NULL) { fprintf(stderr,"Could not open %s for writing\n", buf); return(-1); } for (std::list<std::string>::const_iterator l_itLine = heeks.begin(); l_itLine != heeks.end(); l_itLine++) { fprintf(fp,"%s\n", l_itLine->c_str()); } fclose(fp); } return 0; }
void LfpDisplayNode::handleEvent(int eventType, MidiMessage& event, int sampleNum) { if (eventType == TTL) { uint8* dataptr = event.getRawData(); int eventNodeId = *(dataptr+1); int eventId = *(dataptr+2); int eventChannel = *(dataptr+3); int eventTime = event.getTimeStamp(); int samplesLeft = totalSamples - eventTime; // std::cout << "Received event from " << eventNodeId << ", channel " // << eventChannel << ", with ID " << eventId << std::endl; // int bufferIndex = (displayBufferIndex + eventTime);// % displayBuffer->getNumSamples(); if (eventId == 1) { ttlState |= (1L << eventChannel); } else { ttlState &= ~(1L << eventChannel); } if (samplesLeft + bufferIndex < displayBuffer->getNumSamples()) { // std::cout << bufferIndex << " " << samplesLeft << " " << ttlState << std::endl; displayBuffer->copyFrom(displayBuffer->getNumChannels()-1, // destChannel bufferIndex, // destStartSample arrayOfOnes, // source samplesLeft, // numSamples float(ttlState)); // gain } else { int block2Size = (samplesLeft + bufferIndex) % displayBuffer->getNumSamples(); int block1Size = samplesLeft - block2Size; //std::cout << "OVERFLOW." << std::endl; //std::cout << bufferIndex << " " << block1Size << " " << ttlState << std::endl; displayBuffer->copyFrom(displayBuffer->getNumChannels()-1, // destChannel bufferIndex, // destStartSample arrayOfOnes, // source block1Size, // numSamples float(ttlState)); // gain //std::cout << 0 << " " << block2Size << " " << ttlState << std::endl; displayBuffer->copyFrom(displayBuffer->getNumChannels()-1, // destChannel 0, // destStartSample arrayOfOnes, // source block2Size, // numSamples float(ttlState)); // gain } // std::cout << "ttlState: " << ttlState << std::endl; // std::cout << "Received event from " << eventNodeId << // " on channel " << eventChannel << // " with value " << eventId << // " at timestamp " << event.getTimeStamp() << std::endl; } else if (eventType == TIMESTAMP) { uint8* dataptr = event.getRawData(); int eventNodeId = *(dataptr+1); int eventId = *(dataptr+2); int eventChannel = *(dataptr+3); // update the timestamp for the current buffer: memcpy(&bufferTimestamp, dataptr+4, 4); // double timeInSeconds = double(ts)/Time::getHighResolutionTicksPerSecond(); // //int64 timestamp = ts[0] << 32 + // // ts[1] << 16 + // // ts[2] << 8 + // // ts[3]; // //memcpy(ts, dataptr+4, 1); // std::cout << "Time in seconds is " << timeInSeconds << std::endl; // // std::cout << "Received event from " << eventNodeId << // // " on channel " << eventChannel << // // " with value " << eventId << // // " for time: " << ts << std::endl; } }
void MidiThread::run() { midiPort = MidiOutput::openDevice(0); map<double, double>::iterator tempoIterator = tempos.begin(); double tickLength = tempoIterator->second; double initTempo = 60000 / (tickLength * ppq); tempoIterator++; map<double, std::pair<int, int>>::iterator timeSigIterator = timeSigs.begin(); int timeSigNumerator = timeSigIterator->second.first; int timeSigDenominator = timeSigIterator->second.second; timeSigIterator++; MidiMessage message; // MIDI click intro File clickFile("Click120.mid"); ScopedPointer<FileInputStream> clickStream = clickFile.createInputStream(); double eventTick; double prevTick = 0; double prevDelta = 0; int now = Time::getMillisecondCounter(); if (clickStream) { click.readFrom(*clickStream); short clickPpq = click.getTimeFormat(); MidiMessageSequence clickSequence = MidiMessageSequence(); clickSequence = *click.getTrack(0); int i = 0; while (i < clickSequence.getNumEvents()) { message = clickSequence.getEventPointer(i)->message; eventTick = message.getTimeStamp(); double origBpm = 60000 / (tickLength * clickPpq); double delta = prevDelta + ((eventTick - prevTick) * (origBpm / initTempo)) * tickLength; Time::waitForMillisecondCounter(now + delta); midiPort->sendMessageNow(message); i++; prevTick = eventTick; prevDelta = delta; } } now = Time::getMillisecondCounter(); prevTick = 0; prevDelta = 0; int currentBar = 1; int currentBeat = 1; int currentTick = 0; int quartersPerBar = timeSigNumerator * (timeSigDenominator / 4); int tickStartOfBar = 0; int tickStartNextBar = quartersPerBar * ppq; currentBpm = initTempo; int i = 0; while (i < midiSequence.getNumEvents() && !threadShouldExit()) { std::stringstream ss; ss << currentBpm; content->updateBpm(ss.str()); message = midiSequence.getEventPointer(i)->message; eventTick = message.getTimeStamp(); // number of midi ticks until note if (tempoIterator != tempos.end() && eventTick >= tempoIterator->first) { tickLength = tempoIterator->second; tempoIterator++; } if (timeSigIterator != timeSigs.end() && eventTick >= timeSigIterator->first) { timeSigNumerator = timeSigIterator->second.first; timeSigDenominator = timeSigIterator->second.second; quartersPerBar = timeSigNumerator * (4 / timeSigDenominator); timeSigIterator++; } if (eventTick >= tickStartNextBar) { tickStartOfBar = tickStartNextBar; tickStartNextBar = tickStartOfBar + quartersPerBar * ppq; currentBar = currentBar + static_cast<int> (std::floor((eventTick - tickStartOfBar) / (ppq * quartersPerBar))) + 1; } currentBeat = static_cast<int> (std::floor((eventTick - tickStartOfBar)/ ppq)) % timeSigNumerator + 1; currentTick = static_cast<int> (eventTick) % ppq; if (!message.isMetaEvent() && (currentTick == 0 || currentTick == 120 || currentTick == 240 || currentTick == 360)) { std::stringstream ss; ss << currentBar << ":" << currentBeat << ":" << setfill('0') << setw(3) << currentTick; content->updateBbt(ss.str()); } double origBpm = 60000 / (tickLength * ppq); double delta = prevDelta + ((eventTick - prevTick) * (origBpm / currentBpm)) * tickLength; Time::waitForMillisecondCounter(now + delta); midiPort->sendMessageNow(message); i++; prevTick = eventTick; prevDelta = delta; } delete midiPort; return; }
int main(int argc, char* argv[]) { string printThis = ""; //string which will contain the content to be saved to output file cout << "Path and name of file: "; //String st = "C:\\Program Files (x86)\\Phase Shift\\music\\Paramore\\Paramore - Ignorance\\notes.mid"; //this file was throwing exceptions in Java version //C:\Users\Sinead\Documents\102. Paramore 01_P-I\Paramore - Ignorance // String st = "C:\\Users\\Sinead\\Documents\\102. Paramore 01_P-I\\Paramore - Ignorance\\notes.mid"; //hardcoded for now String st = "C:\\Users\\Sinead\\Documents\\(GHSH_MSL)_09-TIO\\09 - Take It Off\\notes.mid"; //hardcoded for now //String st = "C:\\Program Files (x86)\\Phase Shift\\music\\The Donnas\\09 - Take It Off\\notes.mid"; //hardcoded for now //C:\\Users\\Sinead\\Documents\\(GHSH_MSL)_09-TIO\\09 - Take It Off printThis += st.toStdString() + "\n"; cout << st << "\n"; //load notes.midi File file(st); FileInputStream fiStream(file); MidiFile midiFile; if (!midiFile.readFrom(fiStream)) { cout << "Error: Nothing Loaded"; return 1; } if (midiFile.getNumTracks() == 0) return 1; //set level of difficulty string level = "expert"; //hardcoded for now int lvl = 0; if (level == "easy") { lvl = 4; } else if (level == "medium") { lvl = 5; } else if (level == "hard") { lvl = 6; } else if (level == "expert") { lvl = 7; } else { cout << "invalid level"; return 1; } printThis += "Level: " + level + "\n"; //set instrument string selectInstru = "drums"; if (selectInstru != "guitar" && selectInstru != "bass" && selectInstru != "drums" && selectInstru != "vocals") { cout << "invalid instrument"; return 1; } printThis += "Instrument: " + selectInstru + "\n"; long ticks_per_beat = midiFile.getTimeFormat(); int tracks = midiFile.getNumTracks(); int useTrack = 0; //the index of the mid file which contains the notes for the instrument. Set in following for-loop string timeSig = ""; //the time signature of the song. Set in following for-loop vector<vector<string>> songSections; //timestamps and sections names of the song. Set in following for-loop //use this to store entries of [timestamp, song_section] which will be used to mark the verse, chorus, etc of the tab //populated in the following for-loop for (int n = 0; n < tracks; n++) { const MidiMessageSequence* seq = midiFile.getTrack(n); MidiMessageSequence::MidiEventHolder * event = seq->getEventPointer(0); //get the event 0 for each track MidiMessage m = event->message; String trackName = m.getTextFromTextMetaEvent(); //TRACK WITH INSTRUMENT NOTES if (trackName.toLowerCase().contains(String(selectInstru))) //get indexes of the tracks which contain the songs sections, and drum notes { useTrack = n; //cout << "use Track " << useTrack << "\n"; } //TIME SIGNATURE if (trackName.equalsIgnoreCase("midi_export")) //get information about the song //time signature and tempo are entries 2 and 3, where tick ==0 { for (int nEvent = 1; nEvent < seq->getNumEvents(); nEvent++) { event = seq->getEventPointer(nEvent); //get each event in track MidiMessage m = event->message; double tick = m.getTimeStamp(); if (tick == 0) { //cout << n << " " << nEvent << " " << tick << " " << m.getTextFromTextMetaEvent() << "\n"; //cout << n << " " << nEvent << " isTimeSignatureMetaEvent " << m.isTimeSignatureMetaEvent () << " getTimeSignatureInfo \n"; //<< m.getTimeSignatureInfo() << "\n"; //getTimeSignatureInfo (int &numerator, int &denominator) //cout << n << " " << nEvent << " isTempoMetaEvent " << m.isTempoMetaEvent () << " getTimeSignatureInfo \n"; //<< m.getTimeSignatureInfo() << "\n"; } } } //EVENTS else if (trackName.equalsIgnoreCase("events")) //store the song sections, and the tick values where they start { for (int nEvent = 1; nEvent < seq->getNumEvents(); nEvent++) //loop through all events for this track, in which the TextFromTextMetaEvent() are in the format: [section <song section>] //song section eg: Intro, Main Riff 1, Main Riff 2, etc { //string tick_and_event[2]; vector<string> tick_and_event; event = seq->getEventPointer(nEvent); //get each event in track MidiMessage m = event->message; //the timestamp associated which each song section double tick = m.getTimeStamp(); ostringstream strs; //convert tick to a type string, to add to the tick_and_event array strs << tick; string timestamp = strs.str(); //song section String songSection = m.getTextFromTextMetaEvent(); songSection = songSection.substring(9, songSection.length() - 1); //ie "[section Intro]" is now "Intro" tick_and_event.push_back(timestamp); tick_and_event.push_back(songSection.toStdString()); songSections.push_back(tick_and_event); //songSections.push_back(songSection.toStdString()); //cout << n << " " << nEvent << " " << timestamp << " " << songSection << "\n"; } } } if (timeSig == "") { //no time signature found. Assume 4/4 timeSig = "4/4"; } //create an ArrayList of all tick indexes we want in our tab vector<double> allTimestamps; const MidiMessageSequence* seq = midiFile.getTrack(useTrack); //this is the sequence which contains notes on/off for the selected instrument long lastTick = 0; for (int nEvent = 0; nEvent < seq->getNumEvents(); nEvent++) { MidiMessageSequence::MidiEventHolder * event = seq->getEventPointer(nEvent); //get each event in track MidiMessage message = event->message; //the timestamp associated which each song section const double timestamp = message.getTimeStamp(); if (message.isNoteOn() //just note on timestamps, since for drums, we don't have to worry for duration && find(allTimestamps.begin(), allTimestamps.end(), timestamp) == allTimestamps.end()) //if !allTimestamps.contains(timestamp) { int note = message.getNoteNumber(); int octave = note / 12; if (octave == lvl) { allTimestamps.push_back(timestamp); //cout << nEvent << " " << timestamp << " " << note << " " << octave << " == " << lvl << "\n"; } //max value appears to be 100 = E8 } } //allTimstamps is now all the unique time indexes of notes //TODO: NOT ALL "-" ARE BEING RECORDED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //create a 2d array, containging the timeTick and all notes played for that timeTick, for the whole song //int** masterList = new int[allTimestamps.size()][7] int lengthOfMasterList = allTimestamps.size() + 1; //plus one, to take into account for the individual drum part string **masterList = new string*[lengthOfMasterList]; for (int i = 0; i < allTimestamps.size() + 1; ++i) { masterList[i] = new string[7]; } //string firstColumn[] = {"0", "B |", "FT|", "T2|", "S |", "HH|", "C |"}; masterList[0][0] = "0"; masterList[0][1] = "B |"; masterList[0][2] = "FT|"; masterList[0][3] = "T2|"; masterList[0][4] = "S |"; masterList[0][5] = "HH|"; masterList[0][6] = "C |"; //addArrayToMaster(masterList, firstColumn, 0); for (int i = 0; i < allTimestamps.size(); i++)//loop through all saved tick times { string oneTick[] = { "-", "-", "-", "-", "-", "-", "-" }; ostringstream strs; //convert allTimestamps[i] to a type string strs << allTimestamps[i]; oneTick[0] = strs.str(); for (int nEvent = 0; nEvent < seq->getNumEvents(); nEvent++) //loop through all events in track { MidiMessageSequence::MidiEventHolder * event = seq->getEventPointer(nEvent); //get each event in track MidiMessage message = event->message; //the timestamp associated which each song section const double timestamp = message.getTimeStamp(); if (message.isNoteOn() //just note on timestamps, since for drums, we don't have to worry for duration && timestamp == allTimestamps[i]) //if it's the timestamp we're looking for { //use http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/midi_note_numbers_for_octaves.htm to find the note and octave from getNoteNumber int note = message.getNoteNumber(); int octave = note / 12; if (octave == lvl) { convertToNote(oneTick, note); } } else if (timestamp > allTimestamps[i]) //we've gone past that point in the song { nEvent += seq->getNumEvents(); //break; } } //cout << "add:" << oneTick[0] << oneTick[1] << oneTick[2] << oneTick[3] << oneTick[4] << oneTick[5] << oneTick[6]; addArrayToMaster(masterList, oneTick, i + 1); //i+1, since [0] is the names of the drums } //work with time sig long note_amount = atol(timeSig.substr(0, timeSig.find("/")).c_str()); //convert string to long long note_type = atol(timeSig.substr(timeSig.find("/") + 1).c_str()); //convert string to long. Note_type: crochet, quaver, etc //GENERATE FINAL CONTENT TO BE PRINTED //the amount of --- should be printed in reverse, ie "1---3", the "---" is determined by "3". //if time 1 is 0, 3 is ticks_per_beat, and the ticks per beat is 480, "---" is printed, then "3" //if time 1 is 0, 3 is ticks_per_beat/2, "" , "-" is printed, then "3" //if time 1 is 0, 3 is ticks_per_beat/4, "" , "" is printed, then "3" //every ticks_per_beat*note_amount there should be a bar int noteAmount = 6; //amount of notes defined (base, snare, etc). 1 is the tick time, anything more is a drum int amountOfBarsPerLine = 4; //"complete" will be the final data, which will be printed. It will be masterList, but with bars inserted in keeping with the timeSig, and with the name of the song part vector<vector<string>> completeList; //a 2D vector vector<string> tickTimesUsed; //for (int i = 0; i < lengthOfMasterList; i++) //{ // for (int j = 0; j < noteAmount; j++)//lengthOfMasterList; i++) // { // printThis += masterList[i][j]; // } // printThis += "END \n"; //} // //printThis += "\n\n"; cout << "ticks_per_beat: " << ticks_per_beat << "\n"; cout << "note_amount:" << note_amount << "\n"; cout << "#ticks between notes: " << (ticks_per_beat/note_amount) << "\n"; cout << "note_type: " << note_type << "\n"; cout << "(note_amount*note_type): " << (note_amount*note_type) << "\n"; for (int j = 1; j <= noteAmount; j++) //crash at the top, bass at the bottom, start at index 1 because index 0 contains the timestamp { //TODO fix error where events are in margin int listIndex = 0; //the index we are at in "line" long bar_index = 0; //the index we are in a certain bar. Ie, the first "-" after a bar is at index one int barCount = 0; //the amount of bars, eg "|-------|-------|----" is 3 bars, barCount = 3 vector<string> line; //this will containing bars "|", gaps between notes "-", and the markers for a note "X" or "O" vector<string> eventLine; //this will be the line containing only spaces and song section/event names string start = ""; //this will contain "HH|", "B |", etc, depending on the value of j for (int i = 0; i < lengthOfMasterList; i++)//loop through all saved tick times, for this drum { if (i > 1) //the symbols for the drum kit, and the very first note should be printed without anything else in front of them { long currentNoteTick = atol(masterList[i][0].c_str()); //the tick belonging to the current note long previousNoteTick = atol(masterList[i - 1][0].c_str()); //the tick belonging to the previous note long diff = currentNoteTick - previousNoteTick; while (diff > (ticks_per_beat / note_amount) + 5) //+5, to allow for some time differences { //NOTE line.push_back("-"); bar_index++;//update bar_index to reflect adding the "-" diff -= (ticks_per_beat / note_amount); //seems to be 17 for first bar, 16 for the rest if (j == 1) //EVENT { eventLine.push_back(" "); //have to add an additional gap to eventLine, to keep it the same length as line } if (bar_index == (note_amount*note_type)) //every (note_amount*note_type)+1 character should be a bar line { line.push_back("|"); if (j == 1) //EVENT { eventLine.push_back(" "); //have to add an additional gap to eventLine, to keep it the same length as line } bar_index = 0; //reset bar_index, as we are now in a new bar barCount++; if (barCount == amountOfBarsPerLine) //we have the amount of bars we want in a line. Now move onto a new line { if (j == 1) //EVENT { //NOTE //we want to start new line completeList.insert(completeList.begin() + listIndex, line); //insert the vector "line" at index "listIndex" listIndex++; line.clear(); line.push_back(start); //always have which drum it is, at the start of the line barCount = 0; //reset barCount for the current line //we want to start new line completeList.insert(completeList.begin() + listIndex, eventLine); //insert the vector "eventLine" at index "listIndex" listIndex++; eventLine.clear(); eventLine.push_back(" "); //2 gaps } else { //NOTE //we want to start new line completeList.insert(completeList.begin() + listIndex, line); //insert the vector "line" at index "listIndex" listIndex += j + 1;// + num; //this orders the notes line.clear(); line.push_back(start); //always have which drum it is, at the start of the line barCount = 0; //reset barCount for the current line } } } } if (j == 1) //EVENT { string curTick = masterList[i][0]; string s = getEventAtTick(songSections, curTick); eventLine.push_back(s); tickTimesUsed.push_back(curTick); } } else if (i == 1) //check to see where abouts in the bar the first note should be { long currentNoteTick = atol(masterList[i][0].c_str()); //the tick belonging to the current note long gapBeforeFirst = currentNoteTick % (ticks_per_beat*note_amount); while (gapBeforeFirst > 0) { if (j == 1)// && !tickTimesUsed.contains(""+currentNoteTick)) //EVENT { string curTick = masterList[i][0]; string s = getEventAtTick(songSections, curTick); eventLine.push_back(s); tickTimesUsed.push_back(curTick); } //NOTE line.push_back("-"); bar_index++;//update bar_index to reflect adding the "-" gapBeforeFirst -= (ticks_per_beat / note_amount); } } else if (i == 0)//the very first index of an array for a note, ie "B |", "HH|", etc { start += masterList[i][j]; // "B |", "HH|", etc bar_index--; //printing out the first "|" will make bar_index = 1, when we want it to be 0 } string curTick = masterList[i][0]; if (j == 1 //EVENT && find(tickTimesUsed.begin(), tickTimesUsed.end(), curTick) == tickTimesUsed.end()) //if !allTimestamps.contains(timestamp) { string s = getEventAtTick(songSections, curTick); eventLine.push_back(s); tickTimesUsed.push_back(curTick); } //NOTE line.push_back(masterList[i][j]); bar_index++; //update bar_index to reflect adding the note //if adding the note has ended the bar if (bar_index == (note_amount*note_type)) //every (note_amount*note_type)+1 character should be a bar line { line.push_back("|"); if (j == 1) //EVENT { eventLine.push_back(" "); //have to add an additional gap to eventLine, to keep it the same length as line } bar_index = 0; //reset bar_index, as we are now in a new bar barCount++; //TODO: why is this the same as a section of code above? if (barCount == amountOfBarsPerLine) //a new line { if (j == 1) //EVENT { //NOTE //we want to start new line completeList.insert(completeList.begin() + listIndex, line); //insert the vector "line" at index "listIndex" listIndex++; line.clear(); line.push_back(start); //always have which drum it is, at the start of the line barCount = 0; //reset barCount for the current line //we want to start new line completeList.insert(completeList.begin() + listIndex, eventLine); //insert the vector "eventLine" at index "listIndex" listIndex++; eventLine.clear(); eventLine.push_back(" "); //2 gaps } else { //NOTE //we want to start new line completeList.insert(completeList.begin() + listIndex, line); //insert the vector "line" at index "listIndex" listIndex += j + 1;// + num; //this orders the notes line.clear(); line.push_back(start); //always have which drum it is, at the start of the line barCount = 0; //reset barCount for the current line } } } if (i == lengthOfMasterList - 1) //the very last index of an array for a note. Could be a note, or a "-" { //cout << "true"; //we want to add this bar to the arrayList, because it is the end, regardless if it's a full bar completeList.insert(completeList.begin() + listIndex, line); //insert the vector "line" at index "listIndex" listIndex += j; //this orders the notes line.clear(); line.push_back(start); //always have which drum it is, at the start of the line barCount = 0; //reset barCount for the current line } } } vector<vector<string>>::iterator it1; vector<string>::iterator it2; int i = 0; for (it1 = completeList.begin(); it1 != completeList.end(); ++it1) { if (i % (noteAmount + 1) == 0)//a new section. Add a gap to make it easier to read. Plus 1, for event line { //cout << "\n"; //print a new line printThis += "\n"; } string line = ""; //reset line for (it2 = (*it1).begin(); it2 != (*it1).end(); ++it2) //create a whole line to print { line += (*it2); //a single character } std::tr1::regex rx(".*[a-z]+.*"); //the line contains a note bool containsNote = regex_match(line.begin() + 3, line.end(), rx); line.substr(3); if (line.find("O") != string::npos || line.find("X") != string::npos || containsNote) { //cout << line << "\n"; printThis += line + "\n"; } i++; } ofstream myfile; myfile.open("C:\\Users\\Sinead\\Desktop\\example.txt"); myfile << printThis; myfile.close(); for (int i = 0; i < lengthOfMasterList; ++i) { delete[] masterList[i]; } delete[] masterList; cout << "end\n"; char c; cin >> c; return 0; }
const String getTimestamp(const MidiMessage &m) { return (String::formatted (" Time(%.6d)", m.getTimeStamp())); }
//============================================================================== int main (int argc, char* argv[]) { if (argc != 3) { cout << "Usage: <prog> <midi input file> <wav output file>" << endl; return 0; } File inMidiFile = File(argv[1]); File outWavFile = File(argv[2]); //File inMidiFile = File("C:\\Users\\GeorgeKrueger\\Documents\\GitHub\\pymusic\\out.mid"); //File outWavFile = File("C:\\Users\\GeorgeKrueger\\Documents\\GitHub\\pymusic\\out.wav"); FileInputStream fileStream(inMidiFile); juce::MidiFile midiFile; midiFile.readFrom(fileStream); int numTracks = midiFile.getNumTracks(); midiFile.convertTimestampTicksToSeconds(); std::cout << "Opened midi file: " << inMidiFile.getFileName() << " Tracks: " << numTracks << std::endl;; playHead.posInfo.bpm = 120; playHead.posInfo.isPlaying = true; playHead.posInfo.timeInSamples = 0; playHead.posInfo.timeInSeconds = 0; playHead.posInfo.timeSigNumerator = 4; playHead.posInfo.timeSigDenominator = 4; for (int i = 0; i < numTracks; ++i) { const juce::MidiMessageSequence* msgSeq = midiFile.getTrack(i); double trackLengthSeconds = 0; String plugFile = ""; int program = 0; for (int j = 0; j < msgSeq->getNumEvents(); ++j) { juce::MidiMessageSequence::MidiEventHolder* midiEventHolder = msgSeq->getEventPointer(j); juce::MidiMessage midiMsg = midiEventHolder->message; if (midiMsg.isMetaEvent() && midiMsg.getMetaEventType() == 0x04) { // Instrument meta event int instrLength = midiMsg.getMetaEventLength(); const juce::uint8* instrChars = midiMsg.getMetaEventData(); String instrName((char*)instrChars, instrLength); plugFile = instrName; } if (midiMsg.isMetaEvent() && midiMsg.isEndOfTrackMetaEvent()) { //int oetDataLength = midiMsg.getMetaEventLength(); //const uint8* oetData = midiMsg.getMetaEventData(); //std::cout << "Found end of track event data size: " << oetDataLength << " data: " << oetData << std::endl; trackLengthSeconds = midiMsg.getTimeStamp(); std::cout << "Track length in seconds: " << trackLengthSeconds << std::endl; } } if (trackLengthSeconds == 0) { std::cerr << "Skipping track " << i << " since it has zero length" << std::endl; continue; } if (plugFile.isEmpty()) { plugFile = "C:\\VST\\helm.dll"; std::cout << "No plug found for track. Defaulting to: " << plugFile << std::endl; //std::cerr << "Skipping track " << i << ". No instrument found." << std::endl; //continue; } else { std::cout << "Found plugin file '" << plugFile << "' from track " << i << std::endl; } OwnedArray<PluginDescription> results; VSTPluginFormat vstFormat; vstFormat.findAllTypesForFile(results, plugFile); if (results.size() > 0) { std::cout << "Found " << results.size() << " plugin(s) in file '" << plugFile << "'" << std::endl; int blockSize = 1024; double sampleRate = 44100; int totalSizeInSamples = ((static_cast<int>(44100 * trackLengthSeconds) / 1024) + 1) * 1024; cout << "Total samples to render " << totalSizeInSamples << endl; juce::AudioPluginInstance* plugInst = vstFormat.createInstanceFromDescription(*results[0], sampleRate, blockSize); if (!plugInst) { cout << "Failed to load plugin " << plugFile << endl; continue; } AudioProcessorGraph* graph = new AudioProcessorGraph(); graph->setPlayConfigDetails(0, 2, sampleRate, blockSize); graph->setPlayHead(&playHead); graph->addNode(plugInst, 1000); int AUDIO_IN_ID = 101; int AUDIO_OUT_ID = 102; int MIDI_IN_ID = 103; juce::AudioPluginInstance* audioInNode = new AudioGraphIOProcessor(AudioGraphIOProcessor::audioInputNode); juce::AudioPluginInstance* audioOutNode = new AudioGraphIOProcessor(AudioGraphIOProcessor::audioOutputNode); juce::AudioPluginInstance* midiInNode = new AudioGraphIOProcessor(AudioGraphIOProcessor::midiInputNode); graph->addNode(audioInNode, AUDIO_IN_ID); graph->addNode(audioOutNode, AUDIO_OUT_ID); graph->addNode(midiInNode, MIDI_IN_ID); graph->addConnection(AUDIO_IN_ID, 0, 1000, 0); graph->addConnection(AUDIO_IN_ID, 1, 1000, 1); graph->addConnection(MIDI_IN_ID, AudioProcessorGraph::midiChannelIndex, 1000, AudioProcessorGraph::midiChannelIndex); graph->addConnection(1000, 0, AUDIO_OUT_ID, 0); graph->addConnection(1000, 1, AUDIO_OUT_ID, 1); plugInst->setCurrentProgram(program); int numInputChannels = plugInst->getTotalNumInputChannels(); int numOutputChannels = plugInst->getTotalNumOutputChannels(); cout << "----- Plugin Information -----" << endl; cout << "Input channels : " << numInputChannels << endl; cout << "Output channels : " << numOutputChannels << endl; cout << "Num Programs: " << plugInst->getNumPrograms() << endl; cout << "Current program: " << plugInst->getCurrentProgram() << endl; int numParams = plugInst->getNumParameters(); cout << "Num Parameters: " << numParams << endl; for (int p = 0; p < numParams; ++p) { std::cout << "Param " << p << ": " << plugInst->getParameterName(p); if (!plugInst->getParameterLabel(p).isEmpty()) { cout << "(" << plugInst->getParameterLabel(p) << ")"; } cout << " = " << plugInst->getParameter(p) << endl; } cout << "-----------------------------" << endl; int maxChannels = std::max(numInputChannels, numOutputChannels); AudioBuffer<float> entireAudioBuffer(maxChannels, totalSizeInSamples); entireAudioBuffer.clear(); unsigned int midiSeqPos = 0; graph->releaseResources(); graph->prepareToPlay(sampleRate, blockSize); cout << "Num midi events: " << msgSeq->getNumEvents() << endl; // Render the audio in blocks for (int t = 0; t < totalSizeInSamples; t += blockSize) { //cout << "processing block " << t << " to " << t + blockSize << endl; MidiBuffer midiBuffer; for (int j = midiSeqPos; j < msgSeq->getNumEvents(); ++j) { MidiMessageSequence::MidiEventHolder* midiEventHolder = msgSeq->getEventPointer(j); MidiMessage midiMsg = midiEventHolder->message; int samplePos = static_cast<int>(midiMsg.getTimeStamp() * sampleRate); if (samplePos >= t && samplePos < t + blockSize) { if (midiMsg.isNoteOnOrOff()) { if (midiMsg.isNoteOn()) { cout << "note on event (" << midiMsg.getNoteNumber() << ") at " << samplePos << "(" << midiMsg.getTimeStamp() << "s) bufferpos=" << (samplePos - t) << endl; } else if (midiMsg.isNoteOff()) { cout << "note off event (" << midiMsg.getNoteNumber() << ") at " << samplePos << "(" << midiMsg.getTimeStamp() << "s) bufferpos=" << (samplePos - t) << endl; } midiBuffer.addEvent(midiMsg, samplePos - t); } else if (midiMsg.isProgramChange()) { program = midiMsg.getProgramChangeNumber(); plugInst->setCurrentProgram(program); } midiSeqPos++; } else { break; } } playHead.posInfo.timeInSamples = t; playHead.posInfo.timeInSeconds = t / sampleRate; AudioBuffer<float> blockAudioBuffer(entireAudioBuffer.getNumChannels(), blockSize); blockAudioBuffer.clear(); graph->processBlock(blockAudioBuffer, midiBuffer); for (int ch = 0; ch < entireAudioBuffer.getNumChannels(); ++ch) { entireAudioBuffer.addFrom(ch, t, blockAudioBuffer, ch, 0, blockSize); } } if (outWavFile.exists()) { outWavFile.deleteFile(); } FileOutputStream* fileOutputStream = outWavFile.createOutputStream(); WavAudioFormat wavFormat; StringPairArray metadataValues; juce::AudioFormatWriter* wavFormatWriter = wavFormat.createWriterFor( fileOutputStream, sampleRate, 2, 16, metadataValues, 0); bool writeAudioDataRet = wavFormatWriter->writeFromAudioSampleBuffer(entireAudioBuffer, 0, entireAudioBuffer.getNumSamples()); wavFormatWriter->flush(); cout << "Done writing to output file " << outWavFile.getFileName() << " . Write return value: " << (int)writeAudioDataRet << endl; delete wavFormatWriter; } else { cerr << "Could not find plugin from file " << plugFile << endl; } } return 0; }