/*
* The GPLv3 licence :
* -----------------
* Copyright (c) 2009 Ricardo Dias
*
* This file is part of MuVis.
*
* MuVis is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MuVis is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MuVis. If not, see <http://www.gnu.org/licenses/>.
*/
package muvis.analyser.processor;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import muvis.NBTreeManager;
import muvis.Environment;
import muvis.analyser.loader.Loader;
import muvis.database.MusicLibraryDatabaseManager;
import muvis.util.MP3AudioFile;
import muvis.util.Observable;
import muvis.util.Observer;
import nbtree.NBPoint;
import nbtree.NBTree;
import nbtree.exceptions.NBTreeException;
public class ContentUnprocessor implements Observer, Observable {
private ArrayList<Observer> observers;
private ExecutorService threadPool;
private MusicLibraryDatabaseManager dbManager;
private NBTree tracksNBTree, albumsTree, artistsTree;
protected File[] filesToProcess;
public ContentUnprocessor() {
observers = new ArrayList<Observer>();
threadPool = Executors.newFixedThreadPool(1);
dbManager = Environment.getEnvironmentInstance().getDatabaseManager();
NBTreeManager nbtreeManager = Environment.getEnvironmentInstance().getNbtreesManager();
albumsTree = nbtreeManager.getNBTree("albumsNBTree");
artistsTree = nbtreeManager.getNBTree("artistsNBTree");
}
class TrackLibraryRemoverThread extends Thread {
@Override
public void run() {
for (int i = 0; i < filesToProcess.length; i++) {
try {
File nextFile = filesToProcess[i];
double key = dbManager.getTrackKey(nextFile.getCanonicalPath());
tracksNBTree.removePoint(key);
dbManager.removeTrack(nextFile.getAbsolutePath());
} catch (IOException ex) {
System.out.println("Couldn't delete this file!");
ex.printStackTrace();
} catch (SQLException ex) {
System.out.println("Couldn't delete this file!");
ex.printStackTrace();
}
}
}
}
class ContentProcessorAlbumsArtistsThread extends Thread {
@Override
public void run() {
//updating the artist and albums descriptors
ArrayList<String> artistNames = dbManager.getAllArtistNames();
ArrayList<double[]> albumsDescriptors = new ArrayList<double[]>();
for (String artist : artistNames) {
ArrayList<String> artistAlbums = dbManager.getArtistAlbums(artist);
for (String album : artistAlbums) {
ArrayList<String> albumTracks = dbManager.getAlbumTracks(artist, album);
int numAlbumTracks = 1;
double[] albumDescriptor = new double[1200];
for (String track : albumTracks) {
double key = dbManager.getTrackKey(track);
try {
NBPoint point = tracksNBTree.lookupPoint(key);
if (point != null) {
double[] trackDescriptor = tracksNBTree.lookupPoint(key).toArray();
albumDescriptor = sum(albumDescriptor, trackDescriptor);
numAlbumTracks++;
} else {
continue;
}
} catch (NBTreeException ex) {
ex.printStackTrace();
}
}
//calculating the new descriptor
for (int j = 0; j < 1200; j++) {
albumDescriptor[j] /= numAlbumTracks;
}
try {
//normalize audio descriptor
albumDescriptor = normalize(albumDescriptor);
double albumKey = dbManager.getAlbumKey(artist, album);
if (albumKey != -1) {
//must remove the key to update it again
albumsTree.removePoint(albumKey);
}
//updating the nbTree with the new point
albumKey = albumsTree.insertPoint(new NBPoint(albumDescriptor));
//updates the database with the new album key
dbManager.setAlbumKey(artist, album, albumKey);
} catch (SQLException ex) {
ex.printStackTrace();
} catch (NBTreeException ex) {
ex.printStackTrace();
}
albumsDescriptors.add(albumDescriptor);
}
double[] artistDescriptor = new double[1200];
for (double[] albumDescriptor : albumsDescriptors) {
artistDescriptor = sum(artistDescriptor, albumDescriptor);
}
//calculating the new descriptor
for (int j = 0; j < 1200; j++) {
artistDescriptor[j] /= albumsDescriptors.size();
}
try {
//normalize artist descriptor
artistDescriptor = normalize(artistDescriptor);
double artistKey = dbManager.getArtistKey(artist);
if (artistKey != -1) {
//remove the artist key so we can update it again
artistsTree.removePoint(artistKey);
}
//insert the new artist key
artistKey = artistsTree.insertPoint(new NBPoint(artistDescriptor));
//updates the key in the artist
dbManager.setArtistKey(artist, artistKey);
} catch (SQLException ex) {
ex.printStackTrace();
} catch (NBTreeException ex) {
ex.printStackTrace();
}
albumsDescriptors.clear();
}
}
}
/**
* These method is invoked when some files must be removed from the library
* @param obs
* @param arg
*/
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof Loader) {
ArrayList<MP3AudioFile> files = (ArrayList<MP3AudioFile>) arg;
filesToProcess = new File[files.size()];
int i = 0;
for (MP3AudioFile file : files) {
filesToProcess[i] = file.getAudioFile();
i++;
}
Executors.newFixedThreadPool(1).execute(new Thread() {
@Override
public void run() {
System.out.println("Removing the specified tracks:");
TrackLibraryRemoverThread removeThread = new TrackLibraryRemoverThread();
removeThread.setPriority(Thread.NORM_PRIORITY);
threadPool.execute(removeThread);
try {
removeThread.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
threadPool.shutdown();
while (!threadPool.isTerminated()) {
try {
sleep(30000);
} catch (InterruptedException ex) {
ex.printStackTrace();
continue;
}
}
System.out.println("Tracks Removed!");
System.out.println("Re-processing library started!");
if (filesToProcess.length > 0){
new ContentProcessorAlbumsArtistsThread().run();
try {
albumsTree.save();
artistsTree.save();
} catch (IOException ex) {
ex.printStackTrace();
}
}
System.out.println("Re-processing library finished!");
}
});
}
}
@Override
public void registerObserver(Observer obs) {
observers.add(obs);
}
@Override
public void unregisterObserver(Observer obs) {
observers.remove(obs);
}
@Override
public void updateObservers() {
for (Observer obs : observers) {
obs.update(this, null);
}
}
private double[] sum(double[] vec1, double[] vec2) {
if (vec1.length != vec2.length) {
return null;
} else {
double[] result = new double[vec1.length];
for (int i = 0; i < vec1.length; i++) {
result[i] = vec1[i] + vec2[i];
}
return result;
}
}
private double[] normalize(double[] descriptor) {
int max = 200;
int min = 0;
int minNorm = 0;
int maxNorm = 1;
double[] normalizedDescriptor = new double[descriptor.length];
for (int i = 0; i < descriptor.length; i++) {
double parc1 = descriptor[i] - min;
double parc2 = max - min;
double num = minNorm + (parc1 / parc2) * (maxNorm - minNorm);
//update the new position
normalizedDescriptor[i] = num;
}
return normalizedDescriptor;
}
}