/************************************************************************************** PROTUX - THE FREE PROFESSIONAL AUDIO TOOLS FOR LINUX AUTHOR : See AUTHORS file for details This software is distributed under the terms of the GNU General Public License as specified in the COPYING file. ***************************************************************************************/ #include #include #include #include #include #include "Song.hh" #include "BusMonitor.hh" #include #include #include #include #include #include #define MAX_VZOOM 2 // ALL THESE JOG STUFF ARE A MESS. MUST A CLEAN ASAP. THERE MUST BE AN // GENERALIZATION OF JOGS Song::Song(int pMyNumber, Project* pParentProject, QWidget* parent, int pRate, int pBitDepth) : QWidget(parent) { PENTERCONS; myNumber = pMyNumber; QString sMyNumber = ""; sMyNumber.setNum( myNumber ); parentProject=pParentProject; audioSourcesList=new AudioSourcesList(); rate=pRate; bitDepth=pBitDepth; propertiesDialog = new SongPropertiesDialog(rate,bitDepth,false); // VERTICAL BOX WITH 2 COMPONENTS QBoxLayout *mainVBoxLayout = new QVBoxLayout( this, 2); //mainVBoxLayout->setResizeMode(QLayout::FreeResize); QSplitter* splitter = new QSplitter(this); splitter->setOpaqueResize( false ); // INSIDE THE SPLITTER : LEFT SIDE QString pName1 = "Tracks Panels Area ( Song " + sMyNumber + " )"; trackPanelsArea = new TrackPanelsArea( this , splitter, (const char*) pName1);// WAS leftBox); // INSIDE THE SPLITTER : RIGHT SIDE QString pName2 = "Tracks Area ( Song " + sMyNumber + " )"; tracksArea = new TracksArea( this , splitter, (const char*) pName2 ); // WAS rightBox //locator = new Locator( this); //locator->show(); mainVBoxLayout->addWidget(splitter); mainVBoxLayout->activate(); QString projDir = parentProject->get_root_dir(); QString songNum(""); songNum.setNum(myNumber); rootDir = projDir + "/song_" + songNum; audioSourcesDir = rootDir + "/audiosources"; captureDir = rootDir + "/capture"; mtaProj = rootDir + "/mta.schema"; // mtaProj should be called mtaSchemaFilename const char* cRootDir= (const char*) rootDir.latin1(); if ( mkdir(cRootDir, S_IREAD | S_IWRITE | S_IEXEC)<0) { if (errno!=EEXIST) { PERROR("Cannot create dir %s" , (const char*) rootDir.latin1()); } } const char* cAudioSourcesDir = (const char*) audioSourcesDir.latin1(); if (mkdir(cAudioSourcesDir, S_IREAD | S_IWRITE | S_IEXEC)<0) { if (errno!=EEXIST) { PERROR("Cannot create dir %s ",(const char*) audioSourcesDir.latin1()); } } const char* cCaptureDir = (const char*)captureDir.latin1(); if (mkdir(cCaptureDir, S_IREAD | S_IWRITE | S_IEXEC)<0) { if (errno!=EEXIST) { PERROR("Cannot create dir %s",(const char*) captureDir.latin1()); } } artists = ""; title = ""; jogMode = JOG_NONE; gainingTrack = -1; isScrolling = false; jogMode = JOG_NONE; hzoom = GlobalProperties::get_int("HZOOM_LEVEL"); firstBlock = 0; lastBlock=0; workingBlock = 0; takeNumber=0; playBlocksOffset = 0; numTracks=0; editingMode = EDITING_MODE_NORMAL; activeTrackNumber=0; QValueList lst; lst.append( 60 ); lst.append( 300 ); splitter->setSizes( lst ); mtaBaseY = 0; mixer = new Mixer(this); shuttleTimer = new QTimer(this); connect( shuttleTimer , SIGNAL(timeout()), this, SLOT(update_shuttle())); filterSelector = new FilterSelector(tracksArea); busSelector = new BusSelector(tracksArea,mixer); cursorManager = new CursorManager(this); PEXITCONS; } Song::~Song() { PENTERDES; for (int i=0; iset_assoc_track_panel(trackPanel[i]); numTracks++; } init(); PEXIT; return 1; } int Song::load() { PENTER; // TODO : read song's properties.. QFile f(mtaProj); int ntrack=0; if ( f.open(IO_ReadOnly) ) { QTextStream t( &f ); while (!t.eof()) { QString line = t.readLine(); line = line.stripWhiteSpace(); if (line.find("=0) { PMESG("Loading track %d ... : %s",ntrack,line.latin1()); int j1=line.find("name=\"")+6; int j2=line.find("\"",j1); QString pname=line.mid(j1,j2-j1); int j3 = line.find("mute=")+5; int j4 = line.find(" ",j3+1); QString sMute=line.mid(j3,j4-j3); bool mute=(sMute=="1"); int j5 = line.find("solo=")+5; int j6 = line.find(" ",j5+1); QString sSolo=line.mid(j5,j6-j5); bool solo=(sSolo=="1"); int j7 = line.find("gain=")+5; int j8 = line.find(" ",j7+1); QString sGain=line.mid(j7,j8-j7); float gain=sGain.toFloat(); int j9 = line.find("pan=")+4; int j10 = line.find(" ",j9+1); QString sPan=line.mid(j9,j10-j9); float pan=sPan.toFloat(); track[ntrack]= new Track(this, ntrack, pname, ntrack * Track::INITIAL_HEIGHT, Track::INITIAL_HEIGHT); trackPanel[ntrack]= new TrackPanel(track[ntrack]); track[ntrack]->set_assoc_track_panel(trackPanel[ntrack]); track[ntrack]->isMuted=mute; track[ntrack]->isSolo=solo; track[ntrack]->set_gain(gain); track[ntrack]->set_pan(pan); numTracks++; while ((line.find("=0) { bool isTake; int x1 = line.find("source="); if (x1<0) { x1 = line.find("take=")+6; isTake=true; } else { x1+=8; isTake=false; } int x2 = line.find("\"", x1+1); QString sSource=line.mid(x1,x2-x1); int x3 = line.find("TFB=")+4; int x4 = line.find(" ", x3+1); QString sTFB=line.mid(x3,x4-x3); int x5 = line.find("SFB=")+4; int x6 = line.find(" ", x5+1); QString sSFB=line.mid(x5,x6-x5); int x7 = line.find("LEN=")+4; int x8 = line.find(" ", x7+1); QString sLEN=line.mid(x7,x8-x7); long long tfb = (long long) sTFB.toDouble(); long long sfb = (long long) sSFB.toDouble(); long long len = (long long) sLEN.toDouble(); PMESG("Creating clip from [%s] TFB=%d SFB=%d LEN=%d",(const char*) sSource.latin1(),tfb,sfb,len ); Audio* audioSource = audioSourcesList->get_audio_for_source(sSource); if (!audioSource) { audioSource = new Audio(this); int r = audioSource->build(rootDir+(isTake?"/capture":"/audiosources"),sSource); if (r>0) { audioSourcesList->add(audioSource); track[ntrack]->add_clip(audioSource, tfb, sfb, len, isTake); } } else track[ntrack]->add_clip(audioSource, tfb, sfb, len, isTake); } else if (line.find("=0) { int x1=line.find("type=")+6; int x2=line.find("\"",x1); QString fctype=line.mid(x1,x2-x1); FilterController* fc = track[ntrack]->filterChain->add_filter_controller(fctype); while ( fc && (line.find("=0) { int x1=line.find("type=")+6; int x2=line.find("\"",x1); QString cutype=line.mid(x1,x2-x1); PMESG("Adding Curve %s",(const char*)cutype.latin1()); Curve* cu = fc->add_curve(cutype); if (!cu) // curve already exists ! cu = fc->get_curve(cutype); // so use the already existent curve while (cu && (line.find("=0) { int x1=line.find("pos=")+4; int x2=line.find(" ",x1); QString spos=line.mid(x1,x2-x1); int x3=line.find("value=")+6; int x4=line.find(" ",x3); QString sval=line.mid(x3,x4-x3); PMESG("Adding node : address@hidden",(const char*)sval.latin1(),(const char*)spos.latin1()); long long pos= (long long) spos.toDouble(); float val= sval.toFloat(); CurveNode* no=cu->add_node(pos,val); } } } } } } ntrack++; } } } if (editingMode!=EDITING_MODE_TRACK_CURVES) for (int i=0; ifilterChain->deactivate(); // checks if sample rate and bitdepth are OK. If not, ask users to correct it. // this routine might go to recreate();... bool validMode = true; if ((rate!=22050) && (rate!=44100) && (rate!=48000) && (rate!=96000)) validMode=false; if (bitDepth!=16) // FOR FUTURE : .. && pBitDepth!=20,24,32 ... and so on validMode=false; if (!validMode) // ask user about rate and bitDepth { propertiesDialog->raise(); rate=propertiesDialog->get_rate(); bitDepth=propertiesDialog->get_bit_depth(); } init(); PEXIT; return 1; } int Song::save() { PENTER; PMESG("Saving Song MTA schema : %s",(const char*) mtaProj.latin1()); FILE* file = fopen((const char*)mtaProj.ascii(),"w"); if (!file) { PERROR("Cannot Save Song MTA schema#%d" ,myNumber); PEXIT; return -1; } QString mta_schema = get_mta_schema(); char* data = (char*) mta_schema.latin1(); fwrite( data, 1, mta_schema.length(), file ); fclose( file ); QString p = rootDir + "/song.properties"; FILE* file2 = fopen((const char*)p.latin1(),"w"); if (!file2) { PERROR("Cannot Save Song Properties#%d" ,myNumber); PEXIT; return -1; } QString data2 = "rate=44100\nbitDepth=16\n"; // IMPROVE-ME fwrite( data2, 1, data2.length(), file2 ); fclose( file2 ); PEXIT; return 1; } int Song::init() { PENTER; for (int i=0; ifilterChain->deactivate(); } track[activeTrackNumber]->activate(); cursorManager->start(); update_last_block(); PEXIT; return 1; } void Song::recreate() { recreate_tracks(); recreate_panels(); show_zoom_info(); parentProject->get_bus_monitor()->refresh(); clear_root_space(); } void Song::recreate_tracks() { cursorManager->disable(); for (int i=0; irecreate(); } //if (locator) locator->clear(); cursorManager->enable(); } void Song::recreate_panels() { for (int i=0; irecreate(); } void Song::resizeEvent(QResizeEvent* e) { //if (locator) locator->clear(); recreate(); } void Song::mouseMoveEvent(QMouseEvent* e) { int x = e->x(); int y = e->y(); cursorManager->update(x,y); if (editingMode==EDITING_MODE_TRACK_CURVES) { track[activeTrackNumber]->filterChain->followMouse(x,y); } if (jogMode==JOG_NONE) return; switch (jogMode) { case JOG_ZOOM: { update_jog_zoom(e->x(),e->y()); break; } case JOG_SHUTTLE: { update_shuttle_factor(e->x()); break; } case JOG_EDGE: { update_drag_edge(e->x(),e->y()); break; } case JOG_DRAG: { Track* tc = workingClip->parentTrack; workingClip->set_track_first_block(xpos_to_block(e->x()-cursorManager->get_x_offset())); tc->recreate(); break; } case JOG_GAIN: { update_gain(e->y()); trackPanel[gainingTrack]->recreate(); break; } case JOG_PAN: { update_pan(e->x()); trackPanel[gainingTrack]->recreate(); break; } case JOG_GAIN_AND_PAN: { update_pan(e->x()); update_gain(e->y()); trackPanel[gainingTrack]->recreate(); break; } case JOG_VERTICAL_SCROLL: { update_vertical_scroll(e->y()); break; } } } int Song::process_jmb_action(MustuxJogMouseBoardMessage* jmbMesg) { cursorManager->disable(); QString actionName = jmbMesg->label; int parameter = jmbMesg->parameter; int collNumber = jmbMesg->collectedNumber; bool isBeginning = jmbMesg->moment; if (actionName == "EDITING MODE : NORMAL") { for (int i=0; iset_blur(false); editingMode = EDITING_MODE_NORMAL; cursorManager->set_mode(CursorManager::MODE_FLOAT); cursorManager->set_work_cursor_color(250,50,50); for (int i=0; ifilterChain->deactivate(); recreate(); } else if (actionName == "EDITING MODE : TRACK CURVES") { for (int i=0; iset_blur(true); editingMode = EDITING_MODE_TRACK_CURVES; cursorManager->set_mode(CursorManager::MODE_NONE); cursorManager->set_work_cursor_color(255,255,255); for (int i=0; ifilterChain->activate(); recreate(); } // ACTIONS ALLOWED IN ALL EDITING MODES else if (actionName == "CANCEL") { // REVISE-ME /* if (recorder && (recorder->get_status()!=Recorder::REC_NONE)) { recorder->abort(); info("Recorder aborted!"); } */ } else if (actionName == "SONG PROPERTIES") { propertiesDialog->show(); int rate2=propertiesDialog->get_rate(); int bitDepth2=propertiesDialog->get_bit_depth(); if ((rate2!=rate) || (bitDepth2!=bitDepth)) { // TODO // restart engines, to reflect changes in song's properties PWARN("Changing Rate and BitDepth not implemented yet"); } } else if (actionName == "SELECT BUS IN") { if (isBeginning) { busSelector->start(BusSelector::IN); volatileTrack = get_track_under_mouse(); if (!volatileTrack) volatileTrack = track[activeTrackNumber]; } else { busSelector->stop(); int b = busSelector->get_selected_bus(); if ((b>=0) && (mixer->valid_capture_bus(b))) { volatileTrack->set_bus_in(b); volatileTrack->recreate(); } else { get_default_lcd()->print("INVALID CAPTURE BUS FOR THIS SONG",2); } } } if (actionName == "SELECT BUS OUT") { if (isBeginning) { busSelector->start(BusSelector::OUT); volatileTrack = get_track_under_mouse(); if (!volatileTrack) volatileTrack = track[activeTrackNumber]; } else { busSelector->stop(); int b = busSelector->get_selected_bus(); if ((b>=0) && (mixer->valid_playback_bus(b))) { volatileTrack->set_bus_out(b); volatileTrack->recreate(); } else { get_default_lcd()->print("INVALID PLAYBACK BUS FOR THIS SONG",2); } } } else if (actionName == "CAPTURE FROM BOTH CHANNELS") { volatileTrack = get_track_under_mouse(); if (volatileTrack) { volatileTrack->set_which_channels(MustuxAudioDeviceMapper::STEREO); volatileTrack->recreate(); } } else if (actionName == "CAPTURE ONLY FROM LEFT CHANNEL") { volatileTrack = get_track_under_mouse(); if (volatileTrack) { volatileTrack->set_which_channels(MustuxAudioDeviceMapper::ONLY_LEFT_CHANNEL); volatileTrack->recreate(); } } else if (actionName == "CAPTURE ONLY FROM RIGHT CHANNEL") { volatileTrack = get_track_under_mouse(); if (volatileTrack) { volatileTrack->set_which_channels(MustuxAudioDeviceMapper::ONLY_RIGHT_CHANNEL); volatileTrack->recreate(); } } else if (actionName == "TOUCH") { int trackNumber = collNumber - 1; if (trackNumber>=0) // there is a track number collected !! { if ((trackNumber<=numTracks) && (trackNumber>=0)) touch_track(trackNumber); } else touch(); } else if (actionName == "TOUCH AND CENTER") { int trackNumber = collNumber - 1; if (trackNumber>=0) // there is a track number collected !! { if ((trackNumber<=numTracks) && (trackNumber>=0)) touch_track(trackNumber); } else touch(); center(); } else if (actionName == "ADD TRACK") { add_track(); } else if (actionName == "DELETE TRACK") { delete_track(); } else if (actionName == "HORIZONTAL ZOOM IN") { hzoom_in(); } else if (actionName == "HORIZONTAL ZOOM OUT") { hzoom_out(); } else if (actionName == "VERTICAL ZOOM IN") { vzoom_in(); } else if (actionName == "VERTICAL ZOOM OUT") { vzoom_out(); } else if (actionName == "ZOOM MAGIC KEY") { jog_zoom(); } else if (actionName == "SCROLL RIGHT") { scroll_right(100); } else if (actionName == "SCROLL LEFT") { scroll_left(100); } else if (actionName == "SCROLL DOWN") { scroll_down(); } else if (actionName == "SCROLL UP") { scroll_up(); } else if (actionName == "SHUTTLE") { shuttle(); } else if (actionName == "GO/STOP") { toggle_play(); } else if (actionName == "CENTER OF VIEW") { center(); } else if (actionName == "GO TO BEGIN") { goto_begin(); } else if (actionName == "MUTE/UNMUTE") { mute(); } else if (actionName == "SOLO/UNSOLO") { solo(); } else if (actionName == "TOGGLE TRACK RECORDABLE") { Track* t = get_track_under_mouse(); if (t) { if (t->armed()) t->disarm(); else t->arm(); } parentProject->get_bus_monitor()->refresh(); } else if (actionName == "GAIN") { int trackNumber; if (collNumber>=0) { trackNumber = collNumber - 1 ; } else { trackNumber = activeTrackNumber; } jog_gain(trackNumber); } else if (actionName == "PAN") { int trackNumber; if (collNumber>=0) { trackNumber = collNumber - 1; } else { trackNumber = activeTrackNumber; } jog_pan(trackNumber); } else if (actionName == "GAIN/PAN") { int track; if (collNumber>=0) { track = collNumber*10 + parameter -1; } else { track = parameter-1; } jog_gain_pan(track); } // NORMAL (CLIP) EDITING MODE RESTRICTED ACTIONS if (editingMode == EDITING_MODE_NORMAL) { if (actionName == "SPLIT") { split(); } else if (actionName == "SELECT") { select(); } else if (actionName == "DELETE") { process_delete(); } else if (actionName == "DRAG AND DROP") { drag(false); } else if (actionName == "COPY+DRAG+PASTE") { drag(true); } else if (actionName == "JOG-EXPAND/CONTRACT") { drag_edge(0); // [E] } else if (actionName == "JOG-EXPAND LEFT") { drag_edge(1); // [EW] } else if (actionName == "JOG-EXPAND RIGHT") { drag_edge(2); // [ER] } else if (actionName == "JOG-EXPAND LEFT AND RIGHT") { drag_edge(3); // [WR] } else if (actionName == "JOG VERTICAL SCROLL") { jog_vertical_scroll(); } else { //info("ACTION NOT ALLOWED IN THIS MODE !"); } } else if (editingMode == EDITING_MODE_TRACK_CURVES) { if (actionName == "ADD FILTER CONTROLLER") { if (isBeginning) { filterSelector->start(); } else { filterSelector->stop(); QString filterType = filterSelector->get_selected_filter_type(); //QString s = "Adding " + filterType + " Controller"; //parentProject->get_parent_interface()->defaultLcd->roll_message(s); track[activeTrackNumber]->filterChain->add_filter_controller(filterType); } } else if (actionName == "SELECT FILTER CONTROLLER") { track[activeTrackNumber]->filterChain->select_controller(isBeginning); } else if (actionName == "REMOVE FILTER CONTROLLER") { track[activeTrackNumber]->filterChain->remove_controller(); } else if (actionName == "ADD NODE") { track[activeTrackNumber]->filterChain->add_node(); } else if (actionName == "DRAG AND DROP") { track[activeTrackNumber]->filterChain->drag_node(isBeginning); } else if (actionName == "FILTER SETUP") { track[activeTrackNumber]->filterChain->filter_setup(); } else if (actionName == "NODE SETUP") { track[activeTrackNumber]->filterChain->node_setup(); } } cursorManager->enable(); return 1; } MustuxDrawable* Song::get_tracks_area() { return (MustuxDrawable*) tracksArea; } MustuxDrawable* Song::get_track_panels_area() { return (MustuxDrawable*) trackPanelsArea; } void Song::set_master_gain(float pMasterGain) { } QString Song::get_capture_dir() { return captureDir; } QString Song::get_sources_dir() { return audioSourcesDir; } float Song::get_master_gain() { return masterGain; } int Song::render() { PENTER; QString filename( QFileDialog::getSaveFileName( title + ".praf", "Praf/Wav Files (*.praf *.wav)", this ) ); if ( !filename.isEmpty() ) { const char* fn = filename.ascii(); render(fn); PEXIT; return 1; } PEXIT; return -1; } int Song::render(QString filename) { PENTER; int r = mixer->render_all(filename); if (r<0) { PERROR("Rendering all tracks failed"); } PEXIT; return r; } int Song::import_audio(QString filename, QProgressBar *monitorProgressBar) { PENTER; QString originalName = filename; QString pureName = originalName.mid(originalName.findRev('/')+1); if ( !filename.isEmpty() ) { if ((filename.find("praf",0,false)>=0) || (filename.find("wav" ,0,false)>=0)) { QString targetFilename = audioSourcesDir + "/" + pureName; PMESG("Copying %s to %s", (const char*)originalName.latin1(),(const char*)targetFilename.latin1()); FILE* fSource; FILE* fTarget; fSource = fopen(originalName.latin1(),"r"); fTarget = fopen(targetFilename.latin1(),"w+"); if ((!fSource) || (!fTarget)) { PERROR("Cannot copy this audio source to audio sources dir"); PEXIT; return -1; } size_t r; char buf[4096]; if (monitorProgressBar) monitorProgressBar->setTotalSteps(100); int count=0; fseek(fSource,0,SEEK_END); long long total=ftell(fSource); rewind(fSource); parentProject->get_default_lcd()->print("Copying file ...", 3); while (!feof(fSource)) { r = fread(buf,1,4096,fSource); fwrite(buf,1,r,fTarget); count+=4096; if (count % 5000 == 0) { int i=(int)(100.0*(double)count/total); if (monitorProgressBar) monitorProgressBar->setProgress(i); qApp->processEvents(); } } fclose(fSource); fclose(fTarget); /* // cp originalFilename indicated by User to targetFilename inside song's audio sources dir FILE* pipe_in; QString command = "cp -f \"" + originalName + "\" \"" + targetFilename+"\""; pipe_in = popen (command, "r"); if (!pipe_in) { PERROR("Cannot copy this audio source to audio sources dir"); PEXIT; return -1; } char c; while (!feof(pipe_in)) { fread(&c,1,1,pipe_in); } pclose (pipe_in); */ if (!create_clip(audioSourcesDir, pureName, monitorProgressBar)) { PEXIT; return -1; } } else if ((filename.find("mp3" ,0,false)>=0) || (filename.find("ogg" ,0,false)>=0)) { QString particle; if (filename.find("mp3" ,0,false)>=0) particle="MP3"; else particle="OGG"; QString key = particle + "_DECODE_COMMAND"; char* cs = GlobalProperties::get((char*)key.latin1()); QString decodeCommand(cs); if ((decodeCommand.find("%if")<0) || (decodeCommand.find("%of")<0)) { QString msg = particle + "Decode Command is invalid. Please change it. It must use %if and %of to place input file and output file references , respectively. Exemple : mpg123 -w %of %if"; PERROR(msg.latin1()); PEXIT; return -1; } QString inFilename = originalName; QString outFilename; if (filename.find("mp3" ,0,false)>=0) outFilename= audioSourcesDir + "/" + pureName.replace(QRegExp(".mp3"),".wav"); else outFilename= audioSourcesDir + "/" + pureName.replace(QRegExp(".ogg"),".wav"); decodeCommand = decodeCommand.replace(QRegExp("%if"),"\""+inFilename+"\""); decodeCommand = decodeCommand.replace(QRegExp("%of"),"\""+outFilename+"\""); printf("\n"); PWARN("Executing External command : %s\n",decodeCommand.latin1()); printf("Begin of output :\n"); printf("----------------------------------------\n"); FILE* pipe_in; const char* ccommand = (const char*) decodeCommand.latin1(); pipe_in = popen (ccommand, "r"); if (!pipe_in) { // TODO Jan error message ... PEXIT; return -1; } char c; while (!feof(pipe_in)) fread(&c,1,1,pipe_in); pclose (pipe_in); printf("----------------------------------------\n"); printf("End of output.\n\n"); if (filename.find("mp3" ,0,false)>=0) pureName = pureName.replace(QRegExp(".mp3"),".wav"); else pureName = pureName.replace(QRegExp(".ogg"),".wav"); if (!create_clip(audioSourcesDir,pureName), monitorProgressBar) { PEXIT; return -1; } } else { PERROR("Unkown Audio Format"); PEXIT; return -1; } } PEXIT; return 1; } Project* Song::get_parent_project() { return parentProject; } int Song::get_active_track_number() { return activeTrackNumber; } void Song::touch() { PENTER; touch_x(tracksArea->get_mouse_x()); PEXIT; } void Song::touch_x(int x) { PENTER; for (int i=0; iis_pointed()) || (trackPanel[i]->is_pointed())) { workingBlock = xpos_to_block(x); cursorManager->set_work_at(x); PMESG("Track %d touched : Activating it...",i); touch_track(i); break; } } PEXIT; } void Song::touch_track(int trackNumber) { track[activeTrackNumber]->deactivate(); track[activeTrackNumber]->recreate(); activeTrackNumber=trackNumber; track[activeTrackNumber]->activate(); track[activeTrackNumber]->recreate(); } void Song::touch_in_active_track_panel() // ? REALLY NECESSARY ?? { PENTER; touch_in_active_track_panel(trackPanelsArea->get_mouse_x()); PEXIT; } void Song::touch_in_active_track_panel(int x) // ? REALLY NECESSARY ?? { PENTER; // ? PEXIT; } void Song::set_active_track(int trackNumber) { if ((trackNumber>numTracks) || (trackNumber<0)) return; track[activeTrackNumber]->deactivate(); activeTrackNumber=trackNumber; track[activeTrackNumber]->activate(); } TrackPanel* Song::get_active_track_panel() { return trackPanel[activeTrackNumber]; } TrackPanel* Song::get_track_panel_under_y(int y) { for (int t=0; t= track[t]->real_baseY() ) && ( y <= track[t]->real_baseY() + track[t]->height) ) return trackPanel[t]; } return 0; } Track* Song::get_track(int trackNumber) { if ((trackNumber<0) || (trackNumber>numTracks)) return (Track*) 0; return track[trackNumber]; } void Song::jog_gain(int t) { PENTER; if ((t < 0) || (t > numTracks)) { info("INVALID TRACK"); PEXIT; return; } if (jogMode == JOG_NONE) { PMESG("Starting jog gain for track %d",t); jogMode = JOG_GAIN; gainingTrack = t; origY = tracksArea->get_mouse_y(); origGain = track[t]->get_gain(); } else { PMESG("Finishing jog gain for track %d",track); jogMode = JOG_NONE; gainingTrack = -1; // NOT NEEDED .... THIS LINE CAN BE REMOVED origGain=0; } PEXIT; } void Song::jog_pan(int track) { PENTER; if ((track < 0) || (track > numTracks)) { info("INVALID TRACK"); PEXIT; return; } if (jogMode == JOG_NONE) { PMESG("Starting jog pan for track %d",track); jogMode = JOG_PAN; gainingTrack = track; origX = tracksArea->get_mouse_x(); } else { PMESG("Finishing jog pan for track %d",track); jogMode = JOG_NONE; gainingTrack = -1; // NOT NEEDED .... THIS LINE CAN BE REMOVED } PEXIT; } void Song::jog_gain_pan(int track) { PENTER; if ((track < 0) || (track > numTracks)) { info("INVALID TRACK"); PEXIT; return; } if (jogMode == JOG_NONE) { PMESG("Starting jog gain/pan for track %d",track); jogMode = JOG_GAIN_AND_PAN; gainingTrack = track; origX = tracksArea->get_mouse_x(); origY = tracksArea->get_mouse_y(); } else { PMESG("Finishing jog gain/pan for track %d",track); jogMode = JOG_NONE; gainingTrack = -1; // NOT NEEDED .... THIS LINE CAN BE REMOVED } PEXIT; } void Song::update_gain(int y) { float h = (float) height() * 0.6; float ofy = (float) origY - y; float g = 2.0f * ( ofy / h ) * 12; float fg = g + origGain; track[gainingTrack]->set_gain( fg ); } void Song::update_pan(int x) { int w = (int) (width() * 0.6); int ofx = origX - x; /* if ( abs( ofx ) > 40 ) {*/ float p = -2.0f * (float) (ofx/*-40*/) / w ; track[gainingTrack]->set_pan( p ); /* } if ( abs( ofx ) < -40 ) { float p = 2.0f * (float) (ofx+40) / w ; trackPanel[gainingTrack]->set_pan( p ); }*/ } AudioClip* Song::create_clip(QString audiofileRootdir, QString filename, QProgressBar *monitorProgressBar) { PENTER; Audio* audio = new Audio(this); if (audio->build(audiofileRootdir,filename,monitorProgressBar)<0) { PERROR("File not Found : %s%s",(const char*) audiofileRootdir.latin1(),(const char*) filename.latin1()); return 0; } AudioClip* clip = create_clip(audio); PEXIT; return clip; } AudioClip* Song::create_clip(Audio* au, bool isTake) { return create_clip(au, isTake, track[activeTrackNumber], workingBlock); } AudioClip* Song::create_clip(Audio* au, bool isTake, Track* t, long long insertPos) { PENTER; cursorManager->disable(); // MAYBE NOT NECESSARY. WORTH A TEST... AudioClip* c=0; c = t->add_clip(au, insertPos, isTake); if (!c) { PERROR("Cannot add clip into track %d",activeTrackNumber); } recreate_tracks(); cursorManager->enable(); // MAYBE NOT NECESSARY. WORTH A TEST... PEXIT; return c; } int Song::add_track() { return add_track(numTracks); } int Song::add_track(int newTrackNumber) { PENTER; if (newTrackNumber>=numTracks) newTrackNumber=numTracks; // else if it is before, then.. // else if it is after, then... track[newTrackNumber]= new Track(this,newTrackNumber+1,"Unnamed", numTracks * Track::INITIAL_HEIGHT, Track::INITIAL_HEIGHT); trackPanel[newTrackNumber]= new TrackPanel(track[newTrackNumber]); track[newTrackNumber]->set_assoc_track_panel(trackPanel[newTrackNumber]); numTracks++; recreate(); PEXIT; return 1; } int Song::delete_track() { return delete_track(activeTrackNumber); } int Song::delete_track(int trackNumber) { PENTER; if ((trackNumber<0) || (trackNumber>=numTracks)) return -1; if (track[trackNumber]->clipList) { QString s1; s1.setNum(track[trackNumber]->get_total_clips()); QString mesg = "There are "+s1+" clips on this tracks\nAre you sure you want to delete it ?"; if ( QMessageBox::warning( this, "Delete Track", mesg,"&YES", "&CANCEL", 0 , 0, 1 ) != 0) return -1; } Track* tT = track[trackNumber]; TrackPanel* tP = trackPanel[trackNumber]; if (trackNumber==numTracks-1) { PDEBUG("apagando a ultima..." ); track[numTracks]=(Track*) 0; trackPanel[numTracks]=(TrackPanel*) 0; activeTrackNumber=numTracks-2; numTracks--; } else { for (int i=trackNumber+1; iget_baseY(); int h =track[i-1]->get_height(); track[i-1]=track[i]; trackPanel[i-1]=trackPanel[i]; track[i-1]->set_baseY(b); track[i-1]->set_height(h); } track[numTracks-1]=(Track*)0; trackPanel[numTracks-1]=(TrackPanel*)0; activeTrackNumber=trackNumber - 1 >=0 ? trackNumber - 1 : 0; numTracks--; } delete tT; delete tP; recreate(); PEXIT; return 1; } int Song::split() { PENTER; touch(); track[activeTrackNumber]->split_clip(workingBlock); PEXIT; return 1; } int Song::mute() { PENTER; bool validPoint = false; int pointedTrack = -1; AudioClip* cli = 0 ; for (int i=0; i < numTracks; i++) { if (track[i]->is_pointed()) { validPoint = true; pointedTrack = i; } } if (validPoint) { int pointedBlock = xpos_to_block(tracksArea->get_mouse_x()); cli = track[pointedTrack]->get_clip_under(pointedBlock); } if (cli) { cli->set_muted(!cli->isMuted); track[pointedTrack]->recreate(); return 1; } // no clips pointed ? so check the trackpanels for (int i=0; i < numTracks; i++) { if (trackPanel[i]->is_pointed()) { track[i]->isMuted=!track[i]->isMuted; // should be track[i]->toggle_mute(); trackPanel[i]->recreate(); break; } } PEXIT; return 1; } int Song::solo() { for (int i=0; i < numTracks; i++) { if (trackPanel[i]->is_pointed()) { track[i]->isSolo=!track[i]->isSolo; trackPanel[i]->recreate(); break; } } return 1; } void Song::set_output_spectrogram(Spectrogram* spectrogram) { //FOR LATER get_bus_monitor()->set_output_vu(spectrogram); } // PLAY / RECORD STUFF // WILL BE CALLED TOGGLE_GO int Song::toggle_play() { PENTER; if (mixer->get_status()==Mixer::GOING) { if (any_track_armed()) workingBlock = mixer->get_going_pos(); // forces work cursor to be at the end of recorded audio mixer->force_stop(); info("STOP"); recreate(); } else { update_last_block(); if (mixer->go(workingBlock)<0) { info("Mixer engine cannot be started"); PERROR("Mixer engine cannot be started"); PEXIT; return -1; } info("GOING ..."); PMESG("GOING FROM %d ...",(int)workingBlock); cursorManager->set_mode(CursorManager::MODE_PLAY); } PEXIT; return 1; } // ZOOMING STUFF void Song::hzoom_in() { PENTER; if (hzoom > 0) { hzoom--; PMESG("HZOOM Now is %d",hzoom); show_zoom_info(); cursorManager->set_work_at(block_to_xpos(workingBlock)); recreate_tracks(); } PEXIT; } void Song::vzoom_in() { PENTER; int lasty=0; for (int i=0; ivzoom_in(lasty); lasty+=track[i]->get_height(); } PEXIT; } void Song::vzoom_out() { PENTER; int lasty=0; for (int i=0; ivzoom_out(lasty); lasty+=track[i]->get_height(); } tracksArea->clear(0,lasty,tracksArea->width(),tracksArea->height()-lasty); trackPanelsArea->clear(0,lasty,trackPanelsArea->width(),trackPanelsArea->height()-lasty); PEXIT; } void Song::clear_root_space() { int lasty; if (numTracks>0) lasty = track[numTracks-1]->real_baseY() + track[numTracks-1]->get_height(); else lasty =0; tracksArea->clear(0,lasty,tracksArea->width(),tracksArea->height()-lasty); trackPanelsArea->clear(0,lasty,trackPanelsArea->width(),trackPanelsArea->height()-lasty); } void Song::set_hzoom_level(int level) { PENTER; if ( ( level > 0) && ( level < Peak::MAX_ZOOM_LEVELS - 1)) { hzoom = level; PMESG("HZOOM Now is %d",hzoom); show_zoom_info(); cursorManager->set_work_at(block_to_xpos(workingBlock)); recreate_tracks(); } PEXIT; } void Song::hzoom_out() { PENTER; if ( hzoom < Peak::MAX_ZOOM_LEVELS - 1 ) { hzoom++; PMESG("HZOOM Now is %d",hzoom); show_zoom_info(); cursorManager->set_work_at(block_to_xpos(workingBlock)); recreate_tracks(); } PEXIT; } void Song::jog_zoom() { PENTER; if (jogMode == JOG_NONE) { PMESG("Starting jog_zoom : current hzoom =%d",hzoom); jogZoomTotalX = width(); jogZoomTotalY = height(); int x = tracksArea->get_mouse_x(); verticalJogZoomLastY = tracksArea->get_mouse_y(); baseJogZoomXFactor = hzoom - ((int) ( (float) (jogZoomTotalX - x) / jogZoomTotalX * 100 / 2 ) + 1 ); lastJogZoomXFactor = -1; jogMode = JOG_ZOOM; } else { jogMode = JOG_NONE; } PEXIT; } void Song::update_jog_zoom(int x, int y) { int jzxfactor = (int) ( (float) (jogZoomTotalX - x) / jogZoomTotalX * 100 / 2 ) + 1; if (jzxfactor==0) jzxfactor=1; if (jzxfactor != lastJogZoomXFactor) { lastJogZoomXFactor = jzxfactor; hzoom = jzxfactor + baseJogZoomXFactor; if ( hzoom < 0 ) hzoom = 0; if ( hzoom > Peak::MAX_ZOOM_LEVELS -1 ) hzoom = Peak::MAX_ZOOM_LEVELS - 1; cursorManager->set_work_at(block_to_xpos(workingBlock)); center(); recreate_tracks(); } int vzy = y - verticalJogZoomLastY; if (vzy>10) { vzoom_in(); recreate_tracks(); verticalJogZoomLastY = verticalJogZoomLastY + 10; } else if (vzy<-10) { vzoom_out(); recreate_tracks(); verticalJogZoomLastY = verticalJogZoomLastY - 10; } show_zoom_info(); } void Song::shuttle() { PENTER; if (jogMode == JOG_NONE) { PMESG("Starting shuttle"); jogMode = JOG_SHUTTLE; shuttleTimer->start(25); } else { PMESG("Stopping shuttle"); shuttleTimer->stop(); jogMode = JOG_NONE; } PEXIT; } void Song::update_shuttle_factor(int x) { int w = width(); shuttleFactor = (int) (((float) x / w) * 20 ) - 10; } void Song::update_shuttle() { if (shuttleFactor < 0) scroll_left(shuttleFactor * -20); else scroll_right(shuttleFactor * 20); } void Song::goto_begin() { PENTER; firstBlock = 0; workingBlock = 0; cursorManager->set_work_at(block_to_xpos(workingBlock)); recreate_tracks(); PEXIT; } void Song::goto_end() { PENTER; //firtBlock = ... //for (int i=0; iget_max_block(); //center(); PEXIT; } void Song::center() { PENTER; int w = tracksArea->width(); int half = w/2; long long minimumBlockToCenter = w * Peak::zoomStep[hzoom] / 2; if ( workingBlock >= minimumBlockToCenter ) { int x = block_to_xpos(workingBlock); if (xget_mouse_y(); } else { jogMode = JOG_NONE; } } void Song::update_vertical_scroll(int y) { float f = (float) (y - origY) / (tracksArea->height()); int yToBeCentered = (int) (f * verticalScrollTotalHeight); mtaBaseY = mtaBaseY + (yToBeCentered - tracksArea->height()/2); recreate_tracks(); } void Song::scroll_right(int amount) { PENTER2; firstBlock += amount*Peak::zoomStep[hzoom]; recreate_tracks(); //if (locator) locator->clear(); cursorManager->set_work_at(block_to_xpos(workingBlock)); PEXIT2; } void Song::scroll_left(int amount) { PENTER; firstBlock -= amount*Peak::zoomStep[hzoom]; if (firstBlock<0) firstBlock=0; //if (locator) locator->clear(); recreate_tracks(); cursorManager->set_work_at(block_to_xpos(workingBlock)); PEXIT; } void Song::scroll_down() { mtaBaseY-=30; recreate(); } void Song::scroll_up() { if (mtaBaseY+30>0) mtaBaseY=0; else mtaBaseY+=30; recreate(); } void Song::touch_in_active_track() { PENTER; int x = tracksArea->get_mouse_x(); touch_in_active_track(x); PEXIT; } void Song::touch_in_active_track(int x) { PENTER; workingBlock = xpos_to_block(x); cursorManager->set_work_at(x); PEXIT; } Track* Song::get_track_under_y(int y) { for (int t=0; t= track[t]->real_baseY() ) && ( y <= track[t]->real_baseY() + track[t]->height) ) return track[t]; } return 0; } int Song::select() { PENTER; bool pointingTracks = false; int pointedTrack = -1; AudioClip* cli = 0 ; for (int i=0; i < numTracks; i++) { if (track[i]->is_pointed()) { pointingTracks = true; pointedTrack = i; int x = tracksArea->get_mouse_x(); int pointedBlock = xpos_to_block(x); cli = track[pointedTrack]->get_clip_under(pointedBlock); if (cli) { cli->set_selected(!cli->isSelected); track[pointedTrack]->recreate(); break; } } } if (!pointingTracks) { for (int i=0; i < numTracks; i++) if (trackPanel[i]->is_pointed()) { //FOR LATERtrackPanel[i]->toggle_selected(); break; } } return 1; PEXIT; } int Song::drag(bool pIsCopying) { PENTER; if ( jogMode == JOG_NONE) { jogMode = JOG_DRAG; isCopying=pIsCopying; bool validPoint = false; int pointedTrack = -1; AudioClip* cli = (AudioClip* ) 0 ; for (int i=0; i < numTracks; i++) { if (track[i]->is_pointed()) { validPoint = true; pointedTrack = i; } } if (validPoint) { int x = tracksArea->get_mouse_x(); long long pointedBlock = xpos_to_block(x); //oops shouldnt it be long long ? cli = track[pointedTrack]->get_clip_under(pointedBlock); } if (cli) { workingClip = cli; int cx1 = block_to_xpos( cli->trackFirstBlock ); int cx2 = block_to_xpos( cli->trackLastBlock ); int cy1 = track[pointedTrack]->real_baseY(); int origX = tracksArea->get_mouse_x(); int origY = tracksArea->get_mouse_y(); cursorManager->set_mode(CursorManager::MODE_DRAG, origX, origY, cx2 - cx1, track[pointedTrack]->height - 5, origX - cx1, origY - cy1 ); } else { workingClip = (AudioClip*) 0; } } else { if (workingClip) { int x = tracksArea->get_mouse_x(); int y = tracksArea->get_mouse_y(); long long newInsertBlock = xpos_to_block( x - cursorManager->get_x_offset()); Track* originTrack = workingClip->parentTrack; Track* targetTrack = get_track_under_y(y); if (!targetTrack) { if (!isCopying) { PMESG("Deleting clip %p",workingClip); originTrack->delete_clip(workingClip); originTrack->recreate(); } } else { if (!isCopying) { originTrack->delete_clip(workingClip); originTrack->recreate(); } targetTrack->add_clip(workingClip->audioSource, newInsertBlock, workingClip->sourceFirstBlock, workingClip->get_lenght(), workingClip->isTake); targetTrack->recreate(); } cursorManager->set_mode(CursorManager::MODE_FLOAT); } jogMode = JOG_NONE; workingClip=(AudioClip*) 0; } PEXIT; return true; } int Song::drag_edge(int mode) { PENTER; if (jogMode == JOG_NONE) { bool validPoint = false; int pointedTrack = -1; AudioClip* cli = 0 ; for (int i=0; i < numTracks; i++) { if (track[i]->is_pointed()) { validPoint = true; pointedTrack = i; } } if (validPoint) { int pointedBlock = xpos_to_block(tracksArea->get_mouse_x()); cli = track[pointedTrack]->get_clip_under(pointedBlock); } if (cli) { jogMode = JOG_EDGE; workingClip = cli; int x = tracksArea->get_mouse_x(); if (mode==0) // [E] { int cxm = block_to_xpos( cli->trackFirstBlock + (cli->get_lenght()/2)); if (x < cxm) draggingEdge = LEFT_EDGE; else draggingEdge = RIGHT_EDGE; } else if (mode==1) draggingEdge = LEFT_EDGE; // [EW] else if (mode==2) draggingEdge = RIGHT_EDGE; // [ER] else if (mode==3) draggingEdge = BOTH_EDGES; // [WR] if (draggingEdge==RIGHT_EDGE) { cursorManager->set_x_offset(block_to_xpos( cli->trackLastBlock ) - x); } else { cursorManager->set_x_offset(block_to_xpos( cli->trackFirstBlock ) - x); } } } else { track[activeTrackNumber]->recreate(); jogMode = JOG_NONE; } update_last_block(); PEXIT; return true; } void Song::update_drag_edge(int x, int y) { long long b = xpos_to_block(x + cursorManager->get_x_offset()); long long tfb = workingClip->trackFirstBlock; long long tlb = workingClip->trackLastBlock; long long sfb = workingClip->sourceFirstBlock; long long slb = workingClip->sourceLastBlock; long long totb = workingClip->audioSource->get_total_blocks(); bool recr = false; if (draggingEdge==LEFT_EDGE) { if (sfb + (b-tfb)>=0) { workingClip->set_left_edge(b); recr = true; } } else if (draggingEdge==RIGHT_EDGE) { if (slb + (b-tlb) <= totb ) { workingClip->set_right_edge(b); recr = true; } } else if (draggingEdge==BOTH_EDGES) { long long w = tlb-tfb; if ((sfb + (b-tfb)>=0) && (slb + (b+w-tlb) <= totb )) { workingClip->set_left_edge(b); workingClip->set_right_edge(b+w); recr = true; } } if (recr) { workingClip->parentTrack->recreate();// same as track[pointedtrack]->recreate(); maybe could use this second since is more clear } } int Song::process_delete() { PENTER; AudioClip* cli = 0 ; for (int i=0; i < numTracks; i++) { bool someClipDeleted=false; cli=track[i]->clipList; while (cli) { if (cli->isSelected) { AudioClip* c = cli->next; track[i]->delete_clip(cli); cli=c; someClipDeleted=true; } else cli=cli->next; } if (someClipDeleted) { recreate_tracks(); } } return 1; PEXIT; } int Song::block_to_xpos(long long block) { int x= ((block - firstBlock) / Peak::zoomStep[hzoom]); return x; } long long Song::xpos_to_block(int xpos) { long long b = firstBlock + xpos * Peak::zoomStep[hzoom]; return b; } // get / set stuff int Song::get_hzoom() { return hzoom; } int Song::get_vzoom() // DEPRECATED { return 0; } int Song::get_zoom_step() { return Peak::zoomStep[hzoom]; } long long Song::get_first_block() { return firstBlock; } void Song::set_first_block(long long pos) { firstBlock = (pos >=0) ? pos : 0; } int Song::get_playing_xpos() { long long lo = mixer->get_going_pos(); int pxp = block_to_xpos(lo - playBlocksOffset); return pxp; } long long int Song::get_playing_block() { return mixer->get_going_pos(); } void Song::disable_cursors() { cursorManager->disable(); } void Song::enable_cursors() { cursorManager->enable(); } QString Song::get_mta_schema() { QString output=""; for (int i=0; i < numTracks; i++) { output = output + track[i]->get_schema(); } return output; } int Song::get_total_tracks() { return numTracks; } int Song::get_mouse_x() { return mapFromGlobal(QCursor::pos()).x(); } int Song::get_mouse_y() { return mapFromGlobal(QCursor::pos()).y(); } int Song::get_mta_baseY() { return mtaBaseY; } MustuxLcd* Song::get_default_lcd() { return parentProject->get_default_lcd(); } // This method is here just to avoid the annoying warning "QObject::connect: No such slot Song::cleanupEventFilter()" // when running protux. If anybody knows how to avoid this without implementing this method, please, send an email to // one of protux developers. Thanks a lot. void Song::cleanupEventFilter() { } void Song::info(QString message) { parentProject->get_parent_interface()->info(message); } void Song::info(QString message, int line) { parentProject->get_parent_interface()->info(message, line); } void Song::show_zoom_info() { MustuxDrawable* dataBox = parentProject->get_parent_interface()->dataBox; dataBox->clear(500,0,160,70); QPainter* p = dataBox->painter; p->setPen(QColor(0,0,20)); p->setFont(QFont("Fixed",10)); QString sHzoom; sHzoom.setNum(Peak::zoomStep[hzoom]); sHzoom = "1:" + sHzoom; if (hzoom <= Peak::MAX_ZOOM_USING_SOURCEFILE) { sHzoom = sHzoom + " Micro View"; } p->drawText(510,25,"HZ " + sHzoom); p->drawText(510,45,"VZ x1"); dataBox->update(500,0,160,70); } int Song::get_editing_mode() { return editingMode; } int Song::get_status() { return mixer->get_status(); } long long Song::get_working_block() { return workingBlock; } long long Song::get_last_block() { return lastBlock; } bool Song::any_track_armed() { for (int t=0; tarmed()) return true; return false; } BusMonitor* Song::get_bus_monitor() { return parentProject->get_parent_interface()->get_bus_monitor(); } Track* Song::get_track_under_mouse() { for (int i=0; i < numTracks; i++) { if ((trackPanel[i]->is_pointed()) || (track[i]->is_pointed())) return track[i]; } return (Track*) 0; } Mixer* Song::get_mixer() { return mixer; } int Song::get_rate() { return rate; } int Song::get_bit_depth() { return bitDepth; } int Song::get_block_size() { return (bitDepth/8)*STEREO; } int Song::get_floorY() { if (numTracks>0) { int ct = numTracks-1; // printf("in Song::get_floorY() : numtracks=%d , track[%d]=%p\n",numTracks,ct,track[ct]); int by = track[ct]->get_baseY(); int h = track[ct]->get_height(); int fy = by+h; return fy; } return 0; } void Song::update_last_block() { // find the last blockpos in MTA int numTracks = get_total_tracks(); for (int i=0; iclipList; while (clip) { if (clip->trackLastBlock >= lastBlock) lastBlock = clip->trackLastBlock; clip=clip->next; } } } //eof