Added volume controls and threaded library refresh. Setup for callbacks related to database update functions.

This commit is contained in:
neviyn 2016-02-13 17:42:27 +00:00
parent 6ef92096e9
commit 24070231b6
7 changed files with 117 additions and 64 deletions

View File

@ -0,0 +1,6 @@
package musicplayer;
public interface LibraryCallbackInterface {
void libraryUpdated(boolean successful);
}

View File

@ -1,9 +1,7 @@
package musicplayer;
import musicplayer.db.DatabaseManager;
import musicplayer.db.Gateway;
import musicplayer.model.ExtractedMetadata;
import musicplayer.model.Song;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
@ -11,52 +9,15 @@ import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.TagException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
public class Application {
public final class LibraryUtils {
static final String musicFileExtensionRegex = ".*\\.(mp3|mp4|flac)";
public Application() {
DatabaseManager.init();
List<Song> songs = Gateway.listAllSongs().get();
boolean running = true;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Player player = new Player(null);
while (running) {
try {
System.out.println("Ready to read");
String input = br.readLine();
System.out.println(input);
switch (input) {
case "play":
player.playSong(songs.get(0));
break;
case "stop":
player.stop();
break;
case "pause":
player.pause();
break;
case "resume":
player.resume();
break;
case "quit":
running = false;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Walk through all files and directories recursively and index any music files with a correct extension
*
@ -66,7 +27,7 @@ public class Application {
try {
Files.walk(rootDirectory)
.filter(f -> f.toString().matches(musicFileExtensionRegex))
.map(Application::autoParse).filter(Optional::isPresent).map(Optional::get).forEach(Gateway::addSong);
.map(LibraryUtils::autoParse).filter(Optional::isPresent).map(Optional::get).forEach(Gateway::addSong);
} catch (IOException e) {
e.printStackTrace();
}
@ -78,7 +39,7 @@ public class Application {
* @param targetFile Path to file to extract metadata from.
* @return Metadata contained in targetFile.
*/
public static Optional<ExtractedMetadata> autoParse(Path targetFile) {
private static Optional<ExtractedMetadata> autoParse(Path targetFile) {
Tag audioTags = null;
try {
audioTags = AudioFileIO.read(targetFile.toFile()).getTag();

View File

@ -101,6 +101,21 @@ public class Player {
return playBin.isPlaying() ? (int) (playBin.getClock().getTime().toSeconds() - playBin.getBaseTime().toSeconds()) : 0;
}
/**
* Set the player volume.
* @param volume New volume in percent.
*/
public void setVolume(int volume){
playBin.setVolumePercent(volume);
}
/**
* @return Current Player volume in percent.
*/
public int getVolume(){
return playBin.getVolumePercent();
}
private class InternalThread implements Runnable {
@Override

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="musicplayer.PlayerGUI">
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
@ -10,7 +10,7 @@
<children>
<splitpane id="79ccb">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="760" height="500"/>
</grid>
</constraints>
@ -28,7 +28,10 @@
<children>
<component id="6b9fd" class="javax.swing.JTree" binding="libraryView">
<constraints/>
<properties/>
<properties>
<rootVisible value="true"/>
<showsRootHandles value="false"/>
</properties>
</component>
</children>
</scrollpane>
@ -51,7 +54,7 @@
</splitpane>
<component id="98c55" class="javax.swing.JSlider" binding="seekBar">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<inverted value="false"/>
@ -64,7 +67,7 @@
</component>
<toolbar id="56df">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="-1" height="20"/>
</grid>
</constraints>
@ -91,8 +94,23 @@
<text value="&amp;Next"/>
</properties>
</component>
<component id="c8ecb" class="javax.swing.JSlider" binding="volumeSlider">
<constraints/>
<properties>
<majorTickSpacing value="20"/>
<minorTickSpacing value="10"/>
<paintTicks value="true"/>
<value value="100"/>
</properties>
</component>
</children>
</toolbar>
<component id="e1626" class="javax.swing.JMenuBar" binding="menuBar">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
</form>

View File

@ -12,7 +12,7 @@ import javax.swing.tree.TreeNode;
import java.awt.event.*;
import java.util.*;
public class PlayerGUI implements PlayerCallbackInterface{
public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterface{
private JTree libraryView;
private JTable playList;
private JPanel mainPanel;
@ -20,6 +20,8 @@ public class PlayerGUI implements PlayerCallbackInterface{
private JButton stopButton;
private JButton nextButton;
private JSlider seekBar;
private JSlider volumeSlider;
private JMenuBar menuBar;
private Player player = new Player(this);
private PlaylistTableModel playlistTableModel = new PlaylistTableModel(new ArrayList<>());
@ -35,23 +37,12 @@ public class PlayerGUI implements PlayerCallbackInterface{
public PlayerGUI() {
DatabaseManager.init();
populateLibrary(new TreeMap<>(Gateway.listAllSongsGroupedByAlbum().get()));
libraryView.setRootVisible(false);
libraryView.setToggleClickCount(1);
libraryView.setCellRenderer(new LibraryTreeCellRenderer());
MouseListener mouseListener = new libraryMouseAdapter();
libraryView.addMouseListener(mouseListener);
playList.setModel(playlistTableModel);
playButton.addActionListener(e -> {
if (playList.getSelectedColumn() == -1) {
player.playSong(playlistTableModel.getFirst().get());
}
});
stopButton.addActionListener(e -> player.stop());
nextButton.addActionListener(e -> playNextSong());
resetTree();
volumeSlider.setValue(player.getVolume());
Thread seekBarUpdater = new Thread(() -> {
boolean running = true;
while (running) {
@ -64,6 +55,20 @@ public class PlayerGUI implements PlayerCallbackInterface{
}
});
seekBarUpdater.start();
//Action Listeners
playButton.addActionListener(e -> {
if (playList.getSelectedColumn() == -1) {
player.playSong(playlistTableModel.getFirst().get());
}
});
MouseListener mouseListener = new libraryMouseAdapter();
libraryView.addMouseListener(mouseListener);
stopButton.addActionListener(e -> player.stop());
nextButton.addActionListener(e -> playNextSong());
volumeSlider.addChangeListener(e -> player.setVolume(((JSlider)e.getSource()).getValue()));
populateMenuBar();
}
/**
@ -167,6 +172,23 @@ public class PlayerGUI implements PlayerCallbackInterface{
SwingUtilities.invokeLater(() -> seekBar.setMaximum(seconds));
}
public void refreshLibrary(){
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) libraryView.getModel().getRoot();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("Refreshing Library...");
addNodeToTreeModel(parentNode, newNode);
Thread populateThread = new Thread(() -> {
TreeMap treeMap = new TreeMap<>(Gateway.listAllSongsGroupedByAlbum().get());
SwingUtilities.invokeLater(() -> populateLibrary(treeMap));
});
populateThread.start();
}
@Override
public void libraryUpdated(boolean successful) {
if(successful)
populateLibrary(new TreeMap<>(Gateway.listAllSongsGroupedByAlbum().get()));
}
private class libraryMouseAdapter extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
@ -187,4 +209,25 @@ public class PlayerGUI implements PlayerCallbackInterface{
} catch (NullPointerException ignored) {}
}
}
/**
* Populate the top menu bar with items.
*/
private void populateMenuBar(){
// Tools menu
JMenu tools = new JMenu("Tools");
JMenuItem menuItem = new JMenuItem("Refresh Library");
menuItem.addActionListener(e -> refreshLibrary());
tools.add(menuItem);
menuItem = new JMenuItem("Update Library");
menuItem.setToolTipText("Reindex and refresh library.");
tools.add(menuItem);
// Add everything to the menu bar itself
menuBar.add(tools);
}
}

View File

@ -55,7 +55,6 @@ public class PlaylistTableModel extends AbstractTableModel {
}
public void addSong(Song song){
System.out.println("Adding " + song.toString());
songList.add(song);
fireTableDataChanged();
}

View File

@ -22,6 +22,7 @@ public class ExtractedMetadata {
public final String songFile;
public final String trackNumber;
public final byte[] artwork;
public final String discNumber;
private static final String albumArtRegex = ".*(Cover|Folder|cover|folder)\\.(jpg|png)";
@ -35,6 +36,7 @@ public class ExtractedMetadata {
this.album = audioTags.getFirst(FieldKey.ALBUM);
this.artist = audioTags.getFirst(FieldKey.ARTIST);
this.genre = audioTags.getFirst(FieldKey.GENRE);
this.discNumber = audioTags.getFirst(FieldKey.DISC_NO);
this.songFile = songFile.getAbsolutePath();
byte[] tmpArt = null;
try {
@ -54,16 +56,25 @@ public class ExtractedMetadata {
artwork = tmpArt;
}
/**
* Convert an Image into its' byte array representation.
* @param image Image to convert.
* @return image as byte[].
*/
private static byte[] convertImageToBytes(Image image){
int imageScaleToSize = 50;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){
ImageIO.write(convertToBufferedImage(image.getScaledInstance(imageScaleToSize, imageScaleToSize, Image.SCALE_SMOOTH)), "jpg", baos);
baos.flush();
return baos.toByteArray();
} catch (IOException ignored) {}
return null;
} catch (IOException e) { return null; }
}
/**
* Convert an Image to a BufferedImage.
* @param image Source image.
* @return image as BufferedImage.
*/
private static BufferedImage convertToBufferedImage(Image image) {
BufferedImage newImage = new BufferedImage(
image.getWidth(null), image.getHeight(null),