bool LOS::importMidi(const QString name, bool merge)/*{{{*/ { bool popenFlag; FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag); if (fp == 0) return true; MidiFile mf(fp); bool rv = mf.read(); popenFlag ? pclose(fp) : fclose(fp); if (rv) { QString s(tr("reading midifile\n ")); s += name; s += tr("\nfailed: "); s += mf.error(); QMessageBox::critical(this, QString("LOS"), s); return rv; } // // evaluate song Type (GM, XG, GS, unknown) // MType t = song->mtype(); if (!merge) { t = mf.mtype(); song->setMType(t); } MidiInstrument* instr = 0; for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { MidiInstrument* mi = *i; if ((mi->iname() == "GM" && ((t == MT_UNKNOWN) || (t == MT_GM))) || ((mi->iname() == "GS") && (t == MT_GS)) || ((mi->iname() == "XG") && (t == MT_XG))) { instr = mi; break; } } if (instr == 0) { // the standard instrument files (GM, GS, XG) must be present printf("no instrument, type %d\n", t); return true; //abort(); } MidiFileTrackList* etl = mf.trackList(); int division = mf.division(); // // create MidiTrack and copy events to ->events() // - combine note on/off events // - calculate tick value for internal resolution // int mPort = getFreeMidiPort(); for (iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { MPEventList* el = &((*t)->events); if (el->empty()) continue; // // if we split the track, SYSEX and META events go into // the first target track bool first = true; // somewhat silly and slooow: QList<QPair<int, int> > eventChannelList; if(mPort >= 0 && mPort < MIDI_PORTS) { for (int channel = 0; channel < MIDI_CHANNELS; ++channel) { // // check if there are any events for port/channel in track: // iMPEvent i; for (i = el->begin(); i != el->end(); ++i) { MidiPlayEvent ev = *i; if (ev.type() != ME_SYSEX && ev.type() != ME_META && ev.channel() == channel) break; } if (i == el->end()) continue; MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); track->setOutChannel(channel); track->setOutPort(mPort); MidiPort* mport = &midiPorts[track->outPort()]; // this overwrites any instrument set for this port: mport->setInstrument(instr); EventList* mel = track->events(); buildMidiEventList(mel, el, track, division, first, false, false); first = false; processTrack(track); song->insertTrack(track, -1); //Create the Audio input side of the track Track* input = song->addTrackByName(QString("i").append(track->name()), Track::AUDIO_INPUT, -1, false, false); if(input) { input->setMasterFlag(false); input->setChainMaster(track->id()); track->addManagedTrack(input->id()); } } } if (first) { // // track does only contain non-channel messages // (SYSEX or META) // MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); track->setOutChannel(0); track->setOutPort(mPort); EventList* mel = track->events(); //buildMidiEventList(mel, el, track, division, true); // Do SysexMeta. Don't do loops. buildMidiEventList(mel, el, track, division, true, false, false); processTrack(track); song->insertTrack(track, -1); //Create the Audio input side of the track Track* input = song->addTrackByName(QString("i").append(track->name()), Track::AUDIO_INPUT, -1, false, false); if(input) { input->setMasterFlag(false); input->setChainMaster(track->id()); track->addManagedTrack(input->id()); } } mPort++; //FIXME: Provice a non-iterative way to do this using the new losMidiPorts hash //Or maintain a list of configured or inuse ports while((&midiPorts[mPort])->device() && mPort < MIDI_PORTS) mPort++;//Just incase we have a configured port after an empty one } if (!merge) { TrackList* tl = song->tracks(); if (!tl->empty()) { Track* track = tl->front(); track->setSelected(true); } song->initLen(); int z, n; AL::sigmap.timesig(0, z, n); int tempo = tempomap.tempo(0); transport->setTimesig(z, n); transport->setTempo(tempo); bool masterF = !tempomap.empty(); song->setMasterFlag(masterF); transport->setMasterFlag(masterF); song->updatePos(); composer->reset(); } else { song->initLen(); } return false; }/*}}}*/
void Song::processAutomations(const TrackList &tracklist, MidiTime timeStart, fpp_t) { AutomatedValueMap values; QSet<const AutomatableModel*> recordedModels; TrackContainer* container = this; int tcoNum = -1; switch (m_playMode) { case Mode_PlaySong: break; case Mode_PlayBB: { Q_ASSERT(tracklist.size() == 1); Q_ASSERT(tracklist.at(0)->type() == Track::BBTrack); auto bbTrack = dynamic_cast<BBTrack*>(tracklist.at(0)); auto bbContainer = Engine::getBBTrackContainer(); container = bbContainer; tcoNum = bbTrack->index(); } break; default: return; } values = container->automatedValuesAt(timeStart, tcoNum); TrackList tracks = container->tracks(); Track::tcoVector tcos; for (Track* track : tracks) { if (track->type() == Track::AutomationTrack) { track->getTCOsInRange(tcos, 0, timeStart); } } // Process recording for (TrackContentObject* tco : tcos) { auto p = dynamic_cast<AutomationPattern *>(tco); MidiTime relTime = timeStart - p->startPosition(); if (p->isRecording() && relTime >= 0 && relTime < p->length()) { const AutomatableModel* recordedModel = p->firstObject(); p->recordValue(relTime, recordedModel->value<float>()); recordedModels << recordedModel; } } // Apply values for (auto it = values.begin(); it != values.end(); it++) { if (! recordedModels.contains(it.key())) { it.key()->setAutomatedValue(it.value()); } } }
bool ExportCL(AudacityProject *project, bool stereo, wxString fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec) { int rate = int(project->GetRate() + 0.5); wxWindow *parent = project; TrackList *tracks = project->GetTracks(); wxString command = gPrefs->Read(wxT("/FileFormats/ExternalProgramExportCommand"), wxT("lame - '%f'")); command.Replace(wxT("%f"), fName); /* establish parameters */ int channels = stereo ? 2 : 1; unsigned long totalSamples = (unsigned long)((t1 - t0) * rate + 0.5); unsigned long sampleBytes = totalSamples * channels * SAMPLE_SIZE(int16Sample); /* fill up the wav header */ wav_header header; header.riffID[0] = 'R'; header.riffID[1] = 'I'; header.riffID[2] = 'F'; header.riffID[3] = 'F'; header.riffType[0] = 'W'; header.riffType[1] = 'A'; header.riffType[2] = 'V'; header.riffType[3] = 'E'; header.lenAfterRiff = sampleBytes + 32; header.fmtID[0] = 'f'; header.fmtID[1] = 'm'; header.fmtID[2] = 't'; header.fmtID[3] = ' '; header.formatChunkLen = 16; header.formatTag = 1; header.channels = channels; header.sampleRate = rate; header.bitsPerSample = SAMPLE_SIZE(int16Sample) * 8; header.blockAlign = header.bitsPerSample * header.channels; header.avgBytesPerSec = header.sampleRate * header.blockAlign; header.dataID[0] = 'd'; header.dataID[1] = 'a'; header.dataID[2] = 't'; header.dataID[3] = 'a'; header.dataLen = sampleBytes; FILE *pipe = popen(OSFILENAME(command), "w"); /* write the header */ fwrite( &header, sizeof(wav_header), 1, pipe ); sampleCount maxBlockLen = 44100 * 5; bool cancelling = false; int numWaveTracks; WaveTrack **waveTracks; tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks); Mixer *mixer = new Mixer(numWaveTracks, waveTracks, tracks->GetTimeTrack(), t0, t1, channels, maxBlockLen, true, rate, int16Sample, true, mixerSpec); GetActiveProject()->ProgressShow(_("Export"), selectionOnly ? _("Exporting the selected audio using command-line encoder") : _("Exporting the entire project using command-line encoder")); while(!cancelling) { sampleCount numSamples = mixer->Process(maxBlockLen); if (numSamples == 0) break; samplePtr mixed = mixer->GetBuffer(); char *buffer = new char[numSamples * SAMPLE_SIZE(int16Sample) * channels]; wxASSERT(buffer); // Byte-swapping is neccesary on big-endian machines, since // WAV files are little-endian #if wxBYTE_ORDER == wxBIG_ENDIAN { short *buffer = (short*)mixed; for( int i = 0; i < numSamples; i++ ) buffer[i] = wxINT16_SWAP_ON_BE(buffer[i]); } #endif fwrite( mixed, numSamples * channels * SAMPLE_SIZE(int16Sample), 1, pipe ); int progressvalue = int (1000 * ((mixer->MixGetCurrentTime()-t0) / (t1-t0))); cancelling = !GetActiveProject()->ProgressUpdate(progressvalue); delete[]buffer; } GetActiveProject()->ProgressHide(); delete mixer; pclose( pipe ); return true; }
PyObject* getParts(PyObject*, PyObject* args) { TrackList* tracks = song->tracks(); const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { return NULL; } PyObject* pyparts = Py_BuildValue("[]"); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; if (track->name() != trackname) continue; PartList* parts = track->parts(); for (ciPart p = parts->begin(); p != parts->end(); p++) { Part* part = p->second; MidiPart* mpart = (MidiPart*) part; PyObject* pypart = PyDict_New(); int tick = mpart->tick(); int lentick = mpart->lenTick(); int serialnr = mpart->sn(); PyObject* pstrtick = Py_BuildValue("s", "tick"); PyObject* pitick = Py_BuildValue("i", tick); PyObject* pstrid = Py_BuildValue("s", "id"); PyObject* pstrserial = Py_BuildValue("i", serialnr); PyObject* pstrlen = Py_BuildValue("s", "len"); PyObject* pstrtick2 = Py_BuildValue("i", lentick); PyDict_SetItem(pypart, pstrtick, pitick); PyDict_SetItem(pypart, pstrid, pstrserial); PyDict_SetItem(pypart, pstrlen, pstrtick2); Py_DECREF(pstrtick); Py_DECREF(pitick); Py_DECREF(pstrid); Py_DECREF(pstrserial); Py_DECREF(pstrlen); Py_DECREF(pstrtick2); // Pack midi events into list before wrapping it all up EventList* events = mpart->events(); PyObject* pyevents = Py_BuildValue("[]"); for (ciEvent e = events->begin(); e != events->end(); e++) { PyObject* pyevent = PyDict_New(); // The event structure - a dictionary with keys 'type','tick','data' const Event& event = e->second; unsigned tick = e->first; PyObject* eventdata = Py_BuildValue("[i,i,i]", event.dataA(), event.dataB(), event.dataC()); PyObject* pstrdata = Py_BuildValue("s", "data"); pstrtick = Py_BuildValue("s", "tick"); PyObject* pitickval = Py_BuildValue("i", tick); PyDict_SetItem(pyevent, pstrdata, eventdata); PyDict_SetItem(pyevent, pstrtick, pitickval); Py_DECREF(eventdata); Py_DECREF(pstrdata); Py_DECREF(pstrtick); Py_DECREF(pitickval); switch (event.type()) { case Note: { PyObject* pstrtype = Py_BuildValue("s", "type"); PyObject* pstrnote = Py_BuildValue("s", "note"); PyObject* pstrlen = Py_BuildValue("s", "len"); PyObject* pilentick = Py_BuildValue("i", event.lenTick()); PyDict_SetItem(pyevent, pstrtype, pstrnote); PyDict_SetItem(pyevent, pstrlen, pilentick); Py_DECREF(pstrtype); Py_DECREF(pstrnote); Py_DECREF(pstrlen); Py_DECREF(pilentick); break; } case Controller: { PyObject* pstrtype = Py_BuildValue("s", "type"); PyObject* pstrctrl = Py_BuildValue("s", "ctrl"); PyDict_SetItem(pyevent, pstrtype, pstrctrl); Py_DECREF(pstrtype); Py_DECREF(pstrctrl); break; } default: printf("Event type not supported yet: %d\n", event.type()); break; } PyList_Append(pyevents, pyevent); Py_DECREF(pyevent); } Py_DECREF(pyevents); // Add the event list to the pypart dictionary PyObject* pystrevents = Py_BuildValue("s", "events"); PyDict_SetItem(pypart, pystrevents, pyevents); Py_DECREF(pystrevents); PyList_Append(pyparts, pypart); Py_DECREF(pypart); } return pyparts; } return NULL; }
void Song::processNextBuffer() { m_vstSyncController.setPlaybackJumped( false ); // if not playing, nothing to do if( m_playing == false ) { return; } TrackList trackList; int tcoNum = -1; // track content object number // determine the list of tracks to play and the track content object // (TCO) number switch( m_playMode ) { case Mode_PlaySong: trackList = tracks(); // at song-start we have to reset the LFOs if( m_playPos[Mode_PlaySong] == 0 ) { EnvelopeAndLfoParameters::instances()->reset(); } break; case Mode_PlayBB: if( Engine::getBBTrackContainer()->numOfBBs() > 0 ) { tcoNum = Engine::getBBTrackContainer()-> currentBB(); trackList.push_back( BBTrack::findBBTrack( tcoNum ) ); } break; case Mode_PlayPattern: if( m_patternToPlay != NULL ) { tcoNum = m_patternToPlay->getTrack()-> getTCONum( m_patternToPlay ); trackList.push_back( m_patternToPlay->getTrack() ); } break; default: return; } // if we have no tracks to play, nothing to do if( trackList.empty() == true ) { return; } // check for looping-mode and act if necessary TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine; bool checkLoop = tl != NULL && m_exporting == false && tl->loopPointsEnabled(); if( checkLoop ) { // if looping-mode is enabled and we are outside of the looping // range, go to the beginning of the range if( m_playPos[m_playMode] < tl->loopBegin() || m_playPos[m_playMode] >= tl->loopEnd() ) { setToTime(tl->loopBegin()); m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); m_vstSyncController.setPlaybackJumped( true ); emit updateSampleTracks(); } } if( m_playPos[m_playMode].jumped() ) { m_vstSyncController.setPlaybackJumped( true ); m_playPos[m_playMode].setJumped( false ); } f_cnt_t framesPlayed = 0; const float framesPerTick = Engine::framesPerTick(); while( framesPlayed < Engine::mixer()->framesPerPeriod() ) { m_vstSyncController.update(); float currentFrame = m_playPos[m_playMode].currentFrame(); // did we play a tick? if( currentFrame >= framesPerTick ) { int ticks = m_playPos[m_playMode].getTicks() + ( int )( currentFrame / framesPerTick ); // did we play a whole tact? if( ticks >= MidiTime::ticksPerTact() ) { // per default we just continue playing even if // there's no more stuff to play // (song-play-mode) int maxTact = m_playPos[m_playMode].getTact() + 2; // then decide whether to go over to next tact // or to loop back to first tact if( m_playMode == Mode_PlayBB ) { maxTact = Engine::getBBTrackContainer() ->lengthOfCurrentBB(); } else if( m_playMode == Mode_PlayPattern && m_loopPattern == true && tl != NULL && tl->loopPointsEnabled() == false ) { maxTact = m_patternToPlay->length() .getTact(); } // end of played object reached? if( m_playPos[m_playMode].getTact() + 1 >= maxTact ) { // then start from beginning and keep // offset ticks %= ( maxTact * MidiTime::ticksPerTact() ); // wrap milli second counter setToTimeByTicks(ticks); m_vstSyncController.setPlaybackJumped( true ); } } m_playPos[m_playMode].setTicks( ticks ); if (checkLoop || m_loopRenderRemaining > 1) { m_vstSyncController.startCycle( tl->loopBegin().getTicks(), tl->loopEnd().getTicks() ); // if looping-mode is enabled and we have got // past the looping range, return to the // beginning of the range if( m_playPos[m_playMode] >= tl->loopEnd() ) { if (m_loopRenderRemaining > 1) m_loopRenderRemaining--; ticks = tl->loopBegin().getTicks(); m_playPos[m_playMode].setTicks( ticks ); setToTime(tl->loopBegin()); m_vstSyncController.setPlaybackJumped( true ); emit updateSampleTracks(); } } else { m_vstSyncController.stopCycle(); } currentFrame = fmodf( currentFrame, framesPerTick ); m_playPos[m_playMode].setCurrentFrame( currentFrame ); } if( framesPlayed == 0 ) { // update VST sync position after we've corrected frame/ // tick count but before actually playing any frames m_vstSyncController.setAbsolutePosition( m_playPos[m_playMode].getTicks() + m_playPos[m_playMode].currentFrame() / (double) framesPerTick ); } f_cnt_t framesToPlay = Engine::mixer()->framesPerPeriod() - framesPlayed; f_cnt_t framesLeft = ( f_cnt_t )framesPerTick - ( f_cnt_t )currentFrame; // skip last frame fraction if( framesLeft == 0 ) { ++framesPlayed; m_playPos[m_playMode].setCurrentFrame( currentFrame + 1.0f ); continue; } // do we have samples left in this tick but these are less // than samples we have to play? if( framesLeft < framesToPlay ) { // then set framesToPlay to remaining samples, the // rest will be played in next loop framesToPlay = framesLeft; } if( ( f_cnt_t ) currentFrame == 0 ) { processAutomations(trackList, m_playPos[m_playMode], framesToPlay); // loop through all tracks and play them for( int i = 0; i < trackList.size(); ++i ) { trackList[i]->play( m_playPos[m_playMode], framesToPlay, framesPlayed, tcoNum ); } } // update frame-counters framesPlayed += framesToPlay; m_playPos[m_playMode].setCurrentFrame( framesToPlay + currentFrame ); m_elapsedMilliSeconds[m_playMode] += MidiTime::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo()); m_elapsedTacts = m_playPos[Mode_PlaySong].getTact(); m_elapsedTicks = ( m_playPos[Mode_PlaySong].getTicks() % ticksPerTact() ) / 48; } }
void ControlToolBar::OnRecord(wxCommandEvent &evt) { if (gAudioIO->IsBusy()) { #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) if (mIsLocked) // Stop only if in locked mode. { this->StopPlaying(); // Stop recording. mRecord->PopUp(); } else { //v evt.GetClassInfo(); } #else mRecord->PopUp(); #endif return; } mPlay->Disable(); #if (AUDACITY_BRANDING == BRAND_THINKLABS) mLoopPlay->Disable(); #endif #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) mStop->SetEnabled(mIsLocked); #else mStop->Enable(); #endif mRewind->Disable(); mFF->Disable(); #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) mPause->SetEnabled(mIsLocked); this->EnablePauseCommand(mIsLocked); this->SetRecord(mIsLocked); // If locked, push Record down, else up. #else mPause->Enable(); mRecord->PushDown(); #endif AudacityProject *p = GetActiveProject(); if (p) { TrackList *t = p->GetTracks(); // Don't do this for Audiotouch. Not necessary. The performance hit is in the track being recorded, below. #if (AUDACITY_BRANDING == BRAND_THINKLABS) // For versions that default to dual wave/spectrum display, // switch all tracks to WaveformDisplay on Record, for performance. TrackListIterator iter(t); for (Track* pTrack = iter.First(); pTrack; pTrack = iter.Next()) if (pTrack->GetKind() == Track::Wave) ((WaveTrack*)pTrack)->SetDisplay(WaveTrack::WaveformDisplay); #endif double t0 = p->GetSel0(); double t1 = p->GetSel1(); if (t1 == t0) t1 = 1000000000.0; // record for a long, long time (tens of years) #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) if (p->m_bWantAppendRecording) { t0 = t->GetEndTime(); t1 = 1000000000.0; // record for a long, long time (tens of years) } gAudioIO->SetWantLatencyCorrection(!p->m_bWantAppendRecording); #endif /* TODO: set up stereo tracks if that is how the user has set up * their preferences, and choose sample format based on prefs */ WaveTrackArray newRecordingTracks, playbackTracks; bool duplex; gPrefs->Read("/AudioIO/Duplex", &duplex, true); int recordingChannels = gPrefs->Read("/AudioIO/RecordChannels", 1); if( duplex ) playbackTracks = t->GetWaveTrackArray(false); else playbackTracks = WaveTrackArray(); for( int c = 0; c < recordingChannels; c++ ) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->SetOffset(t0); newTrack->SetRate(p->GetRate()); //v for performance newTrack->SetDisplay(WaveTrack::WaveformAndSpectrumDisplay); newTrack->SetDisplay(WaveTrack::WaveformDisplay); if( recordingChannels == 2 ) { if( c == 0 ) { newTrack->SetChannel(Track::LeftChannel); newTrack->SetLinked(true); } else newTrack->SetChannel(Track::RightChannel); } else { newTrack->SetChannel( Track::MonoChannel ); } newRecordingTracks.Add(newTrack); } int token = gAudioIO->StartStream(playbackTracks, newRecordingTracks, t->GetTimeTrack(), p->GetRate(), t0, t1); bool success = (token != 0); for( unsigned int i = 0; i < newRecordingTracks.GetCount(); i++ ) if (success) t->Add(newRecordingTracks[i]); else delete newRecordingTracks[i]; if (success) { p->SetAudioIOToken(token); mBusyProject = p; SetVUMeters(p); #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) this->SetBackgroundColour(*wxRED); // red for Recording this->Refresh(); #endif } else { // msmeyer: Show error message if stream could not be opened wxMessageBox(_("Error while opening sound device. Please check the input " "device settings and the project sample rate."), _("Error"), wxOK | wxICON_EXCLAMATION, this); SetPlay(false); #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) mStop->Enable(); // In case it was disabled above, based on mIsLocked. #endif SetStop(false); SetRecord(false); } #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) p->OnZoomFitV(); #endif } }
void ControlToolBar::PlayPlayRegion(double t0, double t1, bool looped /* = false */) { if (gAudioIO->IsBusy()) { mPlay->PopUp(); #if (AUDACITY_BRANDING == BRAND_THINKLABS) mLoopPlay->PopUp(); #elif (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) mPlay->Show(); mPause->Hide(); this->EnablePauseCommand(false); #endif return; } mStop->Enable(); mRewind->Disable(); mRecord->Disable(); mFF->Disable(); mPause->Enable(); this->EnablePauseCommand(true); AudacityProject *p = GetActiveProject(); if (p) { TrackList *t = p->GetTracks(); double maxofmins,minofmaxs; // JS: clarified how the final play region is computed; if (t1 == t0) { // msmeyer: When playing looped, we play the whole file, if // no range is selected. Otherwise, we play from t0 to end if (looped) { // msmeyer: always play from start t0 = t->GetStartTime(); } else { // move t0 to valid range if (t0 < 0) t0 = t->GetStartTime(); if (t0 > t->GetEndTime()) t0 = t->GetEndTime(); } // always play to end t1 = t->GetEndTime(); } else { // always t0 < t1 right? // the set intersection between the play region and the // valid range maximum of lower bounds if (t0 < t->GetStartTime()) maxofmins = t->GetStartTime(); else maxofmins = t0; // minimum of upper bounds if (t1 > t->GetEndTime()) minofmaxs = t->GetEndTime(); else minofmaxs = t1; // we test if the intersection has no volume if (minofmaxs <= maxofmins) { // no volume; play nothing return; } else { t0 = maxofmins; t1 = minofmaxs; } } bool success = false; if (t1 > t0) { int token = gAudioIO->StartStream(t->GetWaveTrackArray(false), WaveTrackArray(), t->GetTimeTrack(), p->GetRate(), t0, t1, looped); if (token != 0) { success = true; p->SetAudioIOToken(token); mBusyProject = p; SetVUMeters(p); } else { // msmeyer: Show error message if stream could not be opened wxMessageBox(_("Error while opening sound device. Please check the output " "device settings and the project sample rate."), _("Error"), wxOK | wxICON_EXCLAMATION, this); } } if (!success) { SetPlay(false); SetStop(false); SetRecord(false); } #if (AUDACITY_BRANDING == BRAND_AUDIOTOUCH) if (success) { mPlay->Hide(); mPause->Show(); this->EnablePauseCommand(true); this->SetBackgroundColour(*wxGREEN); // green for Playing this->Refresh(); } #endif } }
int main(int argc, char** argv) { init(argc, argv); if (argc != 3) { google::ShowUsageWithFlags(argv[0]); return 1; } std::string tracks_file = argv[1]; std::string image_format = argv[2]; std::string output_format = FLAGS_output_format; bool ok; TrackList<DrawerPointer> tracks; if (FLAGS_similarity) { // Load tracks. TrackList<SiftPosition> sift_tracks; SiftPositionReader feature_reader; ok = loadTrackList(tracks_file, sift_tracks, feature_reader); CHECK(ok) << "Could not load tracks"; // Convert SIFT features to generic drawable features. siftPositionTracksToDrawers(sift_tracks, tracks); } else if (FLAGS_scale) { // Load tracks. TrackList<ScaleSpacePosition> scale_tracks; ScaleSpacePositionReader feature_reader; ok = loadTrackList(tracks_file, scale_tracks, feature_reader); CHECK(ok) << "Could not load tracks"; // Convert SIFT features to generic drawable features. scaleFeatureTracksToDrawers(scale_tracks, tracks, FLAGS_radius); } else { // Load tracks. TrackList<cv::Point2d> point_tracks; ImagePointReader<double> feature_reader; ok = loadTrackList(tracks_file, point_tracks, feature_reader); CHECK(ok) << "Could not load tracks"; // Convert SIFT features to generic drawable features. translationTracksToDrawers(point_tracks, tracks, FLAGS_radius); } LOG(INFO) << "Loaded " << tracks.size() << " tracks"; // Make a list of random colors. typedef std::vector<cv::Scalar> ColorList; ColorList colors; for (int i = 0; i < int(tracks.size()); i += 1) { colors.push_back(randomColor(BRIGHTNESS, SATURATION)); } // Iterate through frames in which track was observed. TrackListTimeIterator<DrawerPointer> frame(tracks, 0); while (!frame.end()) { // Get the current time. int t = frame.t(); // Load the image. cv::Mat color_image; cv::Mat gray_image; ok = readImage(makeFilename(image_format, t), color_image, gray_image); CHECK(ok) << "Could not read image"; // Get the features. typedef std::map<int, DrawerPointer> FeatureSet; FeatureSet features; frame.getPoints(features); // Draw each one with its color. drawFeatures(color_image, features, colors); if (FLAGS_save) { std::string output_file = makeFilename(output_format, t); ok = cv::imwrite(output_file, color_image); CHECK(ok) << "Could not save image"; } if (FLAGS_display) { cv::imshow("tracks", color_image); cv::waitKey(10); } ++frame; } return 0; }
int ExportOGG::Export(AudacityProject *project, int numChannels, wxString fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, Tags *metadata, int WXUNUSED(subformat)) { double rate = project->GetRate(); TrackList *tracks = project->GetTracks(); double quality = (gPrefs->Read(wxT("/FileFormats/OggExportQuality"), 50)/(float)100.0); wxLogNull logNo; // temporarily disable wxWidgets error messages int updateResult = eProgressSuccess; int eos = 0; FileIO outFile(fName, FileIO::Output); if (!outFile.IsOpened()) { wxMessageBox(_("Unable to open target file for writing")); return false; } // All the Ogg and Vorbis encoding data ogg_stream_state stream; ogg_page page; ogg_packet packet; vorbis_info info; vorbis_comment comment; vorbis_dsp_state dsp; vorbis_block block; // Encoding setup vorbis_info_init(&info); vorbis_encode_init_vbr(&info, numChannels, int(rate + 0.5), quality); // Retrieve tags if (!FillComment(project, &comment, metadata)) { return false; } // Set up analysis state and auxiliary encoding storage vorbis_analysis_init(&dsp, &info); vorbis_block_init(&dsp, &block); // Set up packet->stream encoder. According to encoder example, // a random serial number makes it more likely that you can make // chained streams with concatenation. srand(time(NULL)); ogg_stream_init(&stream, rand()); // First we need to write the required headers: // 1. The Ogg bitstream header, which contains codec setup params // 2. The Vorbis comment header // 3. The bitstream codebook. // // After we create those our responsibility is complete, libvorbis will // take care of any other ogg bistream constraints (again, according // to the example encoder source) ogg_packet bitstream_header; ogg_packet comment_header; ogg_packet codebook_header; vorbis_analysis_headerout(&dsp, &comment, &bitstream_header, &comment_header, &codebook_header); // Place these headers into the stream ogg_stream_packetin(&stream, &bitstream_header); ogg_stream_packetin(&stream, &comment_header); ogg_stream_packetin(&stream, &codebook_header); // Flushing these headers now guarentees that audio data will // start on a new page, which apparently makes streaming easier while (ogg_stream_flush(&stream, &page)) { outFile.Write(page.header, page.header_len); outFile.Write(page.body, page.body_len); } int numWaveTracks; WaveTrack **waveTracks; tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks); Mixer *mixer = CreateMixer(numWaveTracks, waveTracks, tracks->GetTimeTrack(), t0, t1, numChannels, SAMPLES_PER_RUN, false, rate, floatSample, true, mixerSpec); delete [] waveTracks; ProgressDialog *progress = new ProgressDialog(wxFileName(fName).GetName(), selectionOnly ? _("Exporting the selected audio as Ogg Vorbis") : _("Exporting the entire project as Ogg Vorbis")); while (updateResult == eProgressSuccess && !eos) { float **vorbis_buffer = vorbis_analysis_buffer(&dsp, SAMPLES_PER_RUN); sampleCount samplesThisRun = mixer->Process(SAMPLES_PER_RUN); if (samplesThisRun == 0) { // Tell the library that we wrote 0 bytes - signalling the end. vorbis_analysis_wrote(&dsp, 0); } else { for (int i = 0; i < numChannels; i++) { float *temp = (float *)mixer->GetBuffer(i); memcpy(vorbis_buffer[i], temp, sizeof(float)*SAMPLES_PER_RUN); } // tell the encoder how many samples we have vorbis_analysis_wrote(&dsp, samplesThisRun); } // I don't understand what this call does, so here is the comment // from the example, verbatim: // // vorbis does some data preanalysis, then divvies up blocks // for more involved (potentially parallel) processing. Get // a single block for encoding now while (vorbis_analysis_blockout(&dsp, &block) == 1) { // analysis, assume we want to use bitrate management vorbis_analysis(&block, NULL); vorbis_bitrate_addblock(&block); while (vorbis_bitrate_flushpacket(&dsp, &packet)) { // add the packet to the bitstream ogg_stream_packetin(&stream, &packet); // From vorbis-tools-1.0/oggenc/encode.c: // If we've gone over a page boundary, we can do actual output, // so do so (for however many pages are available). while (!eos) { int result = ogg_stream_pageout(&stream, &page); if (!result) { break; } outFile.Write(page.header, page.header_len); outFile.Write(page.body, page.body_len); if (ogg_page_eos(&page)) { eos = 1; } } } } updateResult = progress->Update(mixer->MixGetCurrentTime()-t0, t1-t0); } delete progress;; delete mixer; ogg_stream_clear(&stream); vorbis_block_clear(&block); vorbis_dsp_clear(&dsp); vorbis_info_clear(&info); vorbis_comment_clear(&comment); outFile.Close(); return updateResult; }
bool AudacityPrintout::OnPrintPage(int WXUNUSED(page)) { wxDC *dc = GetDC(); if (!dc) return false; int width, height; dc->GetSize(&width, &height); int rulerScreenHeight = 40; int screenTotalHeight = mTracks->GetHeight() + rulerScreenHeight; double scale = height / (double)screenTotalHeight; int rulerPageHeight = (int)(rulerScreenHeight * scale); Ruler ruler; ruler.SetBounds(0, 0, width, rulerPageHeight); ruler.SetOrientation(wxHORIZONTAL); ruler.SetRange(0.0, mTracks->GetEndTime()); ruler.SetFormat(Ruler::TimeFormat); ruler.SetLabelEdges(true); ruler.Draw(*dc); TrackArtist artist; artist.SetBackgroundBrushes(*wxWHITE_BRUSH, *wxWHITE_BRUSH, *wxWHITE_PEN, *wxWHITE_PEN); ViewInfo viewInfo; viewInfo.selectedRegion = SelectedRegion(); viewInfo.vpos = 0; viewInfo.h = 0.0; viewInfo.screen = mTracks->GetEndTime() - viewInfo.h; viewInfo.total = viewInfo.screen; viewInfo.zoom = viewInfo.lastZoom = width / viewInfo.screen; int y = rulerPageHeight; TrackListIterator iter(mTracks); Track *n = iter.First(); while (n) { wxRect r; r.x = 0; r.y = y; r.width = width; r.height = (int)(n->GetHeight() * scale); artist.DrawTrack(n, *dc, r, &viewInfo, false, false, false, false); dc->SetPen(*wxBLACK_PEN); AColor::Line(*dc, 0, r.y, width, r.y); #ifdef EXPERIMENTAL_OUTPUT_DISPLAY if(MONO_WAVE_PAN(n)){ y += r.height; r.x = 0; r.y = y; r.width = width; r.height = (int)(n->GetHeight(true) * scale); artist.DrawTrack(n, *dc, r, &viewInfo, false, false, false, false); dc->SetPen(*wxBLACK_PEN); AColor::Line(*dc, 0, r.y, width, r.y); } #endif n = iter.Next(); y += r.height; }; return true; }
void ControlToolBar::OnRecord(wxCommandEvent &evt) { if (gAudioIO->IsBusy()) { mRecord->PopUp(); return; } AudacityProject *p = GetActiveProject(); if (p && p->GetCleanSpeechMode()) { size_t numProjects = gAudacityProjects.Count(); bool tracks = (p && !p->GetTracks()->IsEmpty()); if (tracks || (numProjects > 1)) { wxMessageBox(_("Recording in CleanSpeech mode is not possible when a track, or more than one project, is already open."), _("Recording not permitted"), wxOK | wxICON_INFORMATION, this); mRecord->PopUp(); mRecord->Disable(); return; } } if( evt.GetInt() == 1 ) // used when called by keyboard shortcut. Default (0) ignored. mRecord->SetShift(true); if( evt.GetInt() == 2 ) mRecord->SetShift(false); SetRecord(true); if (p) { TrackList *t = p->GetTracks(); TrackListIterator it(t); if(it.First() == NULL) mRecord->SetShift(false); double t0 = p->GetSel0(); double t1 = p->GetSel1(); if (t1 == t0) t1 = 1000000000.0; // record for a long, long time (tens of years) /* TODO: set up stereo tracks if that is how the user has set up * their preferences, and choose sample format based on prefs */ WaveTrackArray newRecordingTracks, playbackTracks; /* REQUIRES PORTMIDI */ // NoteTrackArray midiTracks; bool duplex; gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true); if(duplex){ playbackTracks = t->GetWaveTrackArray(false); /* REQUIRES PORTMIDI */ // midiTracks = t->GetNoteTrackArray(false); } else { playbackTracks = WaveTrackArray(); /* REQUIRES PORTMIDI */ // midiTracks = NoteTrackArray(); } // If SHIFT key was down, the user wants append to tracks int recordingChannels = 0; bool shifted = mRecord->WasShiftDown(); if (shifted) { TrackListIterator it(t); WaveTrack *wt; bool sel = false; double allt0 = t0; // Find the maximum end time of selected and all wave tracks for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave) { wt = (WaveTrack *)tt; if (wt->GetEndTime() > allt0) { allt0 = wt->GetEndTime(); } if (tt->GetSelected()) { sel = true; if (duplex) playbackTracks.Remove(wt); if (wt->GetEndTime() > t0) { t0 = wt->GetEndTime(); } } } } // Use end time of all wave tracks if none selected if (!sel) { t0 = allt0; } // Pad selected/all wave tracks to make them all the same length for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave && (tt->GetSelected() || !sel)) { wt = (WaveTrack *)tt; t1 = wt->GetEndTime(); if (t1 < t0) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->InsertSilence(0.0, t0 - t1); newTrack->Flush(); wt->Clear(t1, t0); wt->Paste(t1, newTrack); delete newTrack; } newRecordingTracks.Add(wt); } } t1 = 1000000000.0; // record for a long, long time (tens of years) } else { recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2); for (int c = 0; c < recordingChannels; c++) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); int initialheight = newTrack->GetHeight(); newTrack->SetOffset(t0); if (recordingChannels <= 2) { newTrack->SetHeight(initialheight/recordingChannels); } else { newTrack->SetMinimized(true); } if (recordingChannels == 2) { if (c == 0) { newTrack->SetChannel(Track::LeftChannel); newTrack->SetLinked(true); } else { newTrack->SetChannel(Track::RightChannel); newTrack->SetTeamed(true); } } else { newTrack->SetChannel( Track::MonoChannel ); } newRecordingTracks.Add(newTrack); } // msmeyer: StartStream calls a callback which triggers auto-save, so // we add the tracks where recording is done into now. We remove them // later if starting the stream fails for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) t->Add(newRecordingTracks[i]); } int token = gAudioIO->StartStream(playbackTracks, newRecordingTracks, /* REQUIRES PORTMIDI */ // midiTracks, t->GetTimeTrack(), p->GetRate(), t0, t1, p); bool success = (token != 0); if (success) { p->SetAudioIOToken(token); mBusyProject = p; SetVUMeters(p); } else { // msmeyer: Delete recently added tracks if opening stream fails if (!shifted) { for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) { t->Remove(newRecordingTracks[i]); delete newRecordingTracks[i]; } } // msmeyer: Show error message if stream could not be opened wxMessageBox(_("Error while opening sound device. " wxT("Please check the input device settings and the project sample rate.")), _("Error"), wxOK | wxICON_EXCLAMATION, this); SetPlay(false); SetStop(false); SetRecord(false); } } }
void ControlToolBar::PlayPlayRegion(double t0, double t1, bool looped /* = false */, bool cutpreview /* = false */, TimeTrack *timetrack /* = NULL */) { SetPlay(true); if (gAudioIO->IsBusy()) { SetPlay(false); return; } if (cutpreview && t0==t1) { SetPlay(false); return; /* msmeyer: makes no sense */ } AudacityProject *p = GetActiveProject(); if (!p) { SetPlay(false); return; // Should never happen, but... } TrackList *t = p->GetTracks(); if (!t) { mPlay->PopUp(); return; // Should never happen, but... } bool hasaudio = false; TrackListIterator iter(t); for (Track *trk = iter.First(); trk; trk = iter.Next()) { if (trk->GetKind() == Track::Wave) { hasaudio = true; break; } } if (!hasaudio) { SetPlay(false); return; // No need to continue without audio tracks } double maxofmins,minofmaxs; // JS: clarified how the final play region is computed; if (t1 == t0) { // msmeyer: When playing looped, we play the whole file, if // no range is selected. Otherwise, we play from t0 to end if (looped) { // msmeyer: always play from start t0 = t->GetStartTime(); } else { // move t0 to valid range if (t0 < 0) t0 = t->GetStartTime(); if (t0 > t->GetEndTime()) t0 = t->GetEndTime(); } // always play to end t1 = t->GetEndTime(); } else { // always t0 < t1 right? // the set intersection between the play region and the // valid range maximum of lower bounds if (t0 < t->GetStartTime()) maxofmins = t->GetStartTime(); else maxofmins = t0; // minimum of upper bounds if (t1 > t->GetEndTime()) minofmaxs = t->GetEndTime(); else minofmaxs = t1; // we test if the intersection has no volume if (minofmaxs <= maxofmins) { // no volume; play nothing return; } else { t0 = maxofmins; t1 = minofmaxs; } } // Can't play before 0...either shifted or latencey corrected tracks if (t0 < 0.0) { t0 = 0.0; } bool success = false; if (t1 > t0) { int token; if (cutpreview) { double beforeLen, afterLen; gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 1.0); gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0); double tcp0 = t0-beforeLen; double tcp1 = (t1+afterLen) - (t1-t0); SetupCutPreviewTracks(tcp0, t0, t1, tcp1); if (mCutPreviewTracks) { token = gAudioIO->StartStream( mCutPreviewTracks->GetWaveTrackArray(false), WaveTrackArray(), /* REQUIRES PORTMIDI */ // NoteTrackArray(), NULL, p->GetRate(), tcp0, tcp1, p, false, t0, t1-t0); } else { // Cannot create cut preview tracks, clean up and exit SetPlay(false); SetStop(false); SetRecord(false); return; } } else { if (!timetrack) { timetrack = t->GetTimeTrack(); } token = gAudioIO->StartStream(t->GetWaveTrackArray(false), WaveTrackArray(), /* REQUIRES PORTMIDI */ // t->GetNoteTrackArray(false), timetrack, p->GetRate(), t0, t1, p, looped); } if (token != 0) { success = true; p->SetAudioIOToken(token); mBusyProject = p; SetVUMeters(p); } else { // msmeyer: Show error message if stream could not be opened wxMessageBox(_( "Error while opening sound device. " wxT("Please check the output device settings and the project sample rate.")), _("Error"), wxOK | wxICON_EXCLAMATION, this); } } if (!success) { SetPlay(false); SetStop(false); SetRecord(false); } }