Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (132)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Loading wav, MP3 and MIDI from INSIDE a .jar file  (Read 3646 times)
0 Members and 1 Guest are viewing this topic.
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Posted 2012-02-10 15:18:29 »

Hi!

I've been extremely successful in making a 2D RPG using some of Bucky's Java Game Development tutorials from TheNewBoston, and generally just pushing my own envelope. Now I've run into a problem I CANNOT solve. I've tried for days now, and when you see what the problem is, you will understand my frustration on the matter.

I've tried to implement a simple MIDIPlayer (class-code supplied at the bottom). The thing works great, until I export to .jar, then no sound plays, but the game keeps chugging away. So, I read up on it, and apparently there's some problem with soundbanks, so I installed Java's deluxe soundbank, to no avail.

NOW, finally I've made it work...but ONLY if the MIDI-files are in the same folder as the .jar file, or a folder in that directory. So I read up on THAT, and it seems you're not supposed to use the standard filepath-method when loading streams (mainly audio) from a .jar file. ONLY if you're referencing an actual file in the folder of the .jar, as per example:
getSequence("SomeMIDIfile.mid");

or in a dedicated folder next to it, as per example:
getSequence("music/SomeMIDIfile.mid");

In fact, you're supposed to reference it using the URL class, and possibly converting that to a URI, and then use the path you can get from there. So I tried that in MANY different forms, and the weirdest thing happens!

The first couple of tries, I got a nullpointer exception, because I was trying to find the correct path-syntax, and when I finally got it, it gave me an error, stating: "Java cannot find the file at the specified location: " and then proceeds to write the correct path, just to mock me Smiley

SO, I'm not getting a nullpointer, because it does find the file...but it doesn't find the file...
The file has been imported into my project on the path: src/snm/music/midis/SomeMIDI.mid
These are the methods I've tried to use to get a usable path (tried ALL of them with every single combination of directories in the path above), and they all end up giving me that terrible error-message when I hit the right "spot".

getSequence(getClass().getResource("/snm/music/midis/SomeMIDIfile.mid").getPath());
getSequence(getClass().getClassLoader().getResource("/snm/music/midis/SomeMIDIfile.mid").getPath());

URL url = new URL("/snm/music/midis/SomeMIDIfile.mid");
getSequence(url.getPath());

As I said, I've also tried to convert the URL to a URI to circumvent the %20's which appear in place of spaces when getting a file-path. I've ALSO tried to rewrite the MIDIPlayer so getSequence doesn't work with File(), but with FileInputStream() instead, still to no avail. It is the same thing with wav-files. Haven't tried MP3s, but logically it should be the same.

I'm at wit's end...and I have exhausted Google. They called me and told me to stop searching for JAVA +MIDI +ARGH over and over again...something about their revenues.

PLEASE, can anyone cast some light on this subject?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
package snm.music.midiplayer;

import java.io.File;
import java.io.IOException;
import javax.sound.midi.*;

public class MidiPlayer implements MetaEventListener {

    // Midi meta event
    public static final int END_OF_TRACK_MESSAGE = 47;

    private Sequencer sequencer;
    private boolean loop;
    private boolean paused;
   
   // load 2 pieces of music
    // This commented out one doesn't give a nullpointer exception, because the path IS right,
    // but it doesn't play anything...The two underneath it, play the MIDI happily when
    // executing the .jar file, but only if the .mid is in the same folder.
   
    //private Sequence worldMusic = getSequence(getClass().getResourceAsStream("/snm/music/midiplayer/MainTheme.mid"));
   private Sequence worldMusic = getSequence("MainTheme.mid");
   private Sequence fightMusic = getSequence("Fighting.mid");

    /**
        Creates a new MidiPlayer object.
    */

    public MidiPlayer() {
        try {
            sequencer = MidiSystem.getSequencer();
            sequencer.open();
            sequencer.addMetaEventListener(this);
        }
        catch ( MidiUnavailableException ex) {
            sequencer = null;
        }
    }


    /**
        Loads a sequence from the file system. Returns null if
        an error occurs.
    */

    public Sequence getSequence(String filename) {
        try {
            return MidiSystem.getSequence(new File(filename));
        }
        catch (InvalidMidiDataException ex) {
            ex.printStackTrace();
            return null;
        }
        catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }


    public void playFight(){
       play(fightMusic, true);
    }
   
    public void playWorldmap(){
       play(worldMusic, true);
    }

    /**
        Plays a sequence, optionally looping. This method returns
        immediately. The sequence is not played if it is invalid.
    */

    public void play(Sequence sequence, boolean loop) {
        if (sequencer != null && sequence != null) {
            try {
                sequencer.setSequence(sequence);
                sequencer.start();
                this.loop = loop;
            }
            catch (InvalidMidiDataException ex) {
                ex.printStackTrace();
            }
        }
    }


    /**
        This method is called by the sound system when a meta
        event occurs. In this case, when the end-of-track meta
        event is received, the sequence is restarted if
        looping is on.
    */

    public void meta(MetaMessage event) {
        if (event.getType() == END_OF_TRACK_MESSAGE) {
            if (sequencer != null && sequencer.isOpen() && loop) {
                sequencer.start();
            }
        }
    }


    /**
        Stops the sequencer and resets its position to 0.
    */

    public void stop() {
         if (sequencer != null && sequencer.isOpen()) {
             sequencer.stop();
             sequencer.setMicrosecondPosition(0);
         }
    }


    /**
        Closes the sequencer.
    */

    public void close() {
         if (sequencer != null && sequencer.isOpen()) {
             sequencer.close();
         }
    }


    /**
        Gets the sequencer.
    */

    public Sequencer getSequencer() {
        return sequencer;
    }


    /**
        Sets the paused state. Music may not immediately pause.
    */

    public void setPaused(boolean paused) {
        if (this.paused != paused && sequencer != null) {
            this.paused = paused;
            if (paused) {
                sequencer.stop();
            }
            else {
                sequencer.start();
            }
        }
    }


    /**
        Returns the paused state.
    */

    public boolean isPaused() {
        return paused;
    }
}

- Jonas
Offline sproingie

JGO Kernel


Medals: 202



« Reply #1 - Posted 2012-02-10 16:54:05 »

Are your .mid files making it into your jar?  If so, getClass().getResource("/snm/music/midis/SomeMIDIfile.mid") should do what you want.  Don't bother constructing URL by hand, since it's not aware of the scheme you need to access it, whereas getResource() is.

If you see it in the jar and you know you have the right resource path, I think your best bet is to step through it in a debugger.  Could be the library you're using is doing something stupid through no fault of your own.
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #2 - Posted 2012-02-10 18:27:07 »

Thanks for your reply! I checked, and the .mid's are in the right place in the .jar, and tried almost exactly what you wrote.

Thing is, the MidiSystem.getSequence() takes a String, so I have to add .getPath() to get the path out of the URL returned by getClass().getResource(). Maybe that's what's destroying the "real" path? I checked what getClass().getResource() returns.

This is it:
file:/D:/Egne%20Projekter/SwordsAndMagic%20v9%20test4.jar!/snm/music/midis/SomeMIDI.mid

Looks right...but it's still not working. I'd think it was because of a missing soundbank in the .jar, but as I mentioned, when I reference a MIDI outside the .jar, it plays perfectly.

- Jonas
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline BoBear2681

JGO Coder


Medals: 19



« Reply #3 - Posted 2012-02-10 22:00:29 »

java.io.File's don't work for resources in jars.  By using new File(...) you're always looking for a physical file on disk, not a resource in the jar.  Change your method to this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
    public Sequence getSequence(String resource) {
        try {
            return MidiSystem.getSequence(getClass().getResource(resource));
        }
        catch (InvalidMidiDataException ex) {
            ex.printStackTrace();
            return null;
        }
        catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }


Then passing in the path to your resource in the jar should work fine.
Offline H3rnst

Senior Newbie


Medals: 1



« Reply #4 - Posted 2012-02-10 22:09:01 »

Quote from: Ultroman
Thing is, the MidiSystem.getSequence() takes a String

Are we talking about the same MidiSystem class? I quote the java api:
Quote
javax.sound.midi.MidiSystem

static Sequence   getSequence(File file)
           Obtains a MIDI sequence from the specified File.
static Sequence   getSequence(InputStream stream)
           Obtains a MIDI sequence from the specified input stream.
static Sequence   getSequence(URL url)
           Obtains a MIDI sequence from the specified URL.

I couldn't find any method getSequence(String s). Anyway, I've recently had to deal with a similar problem when creating a .jar. Here's how I handled it:

1. Suppose you have the .java files in a package named org.myApp.main and the files (image & audio) in org.myApp.main.resources (notice it's the same prefix for both, "org.myApp.main").

2. Since you can use an InputStream with getSequence(InputStream stream), I'd try this:
1  
2  
// No '/' before "resources/mySound.midi"
Secuence mySound = getSequence(getClass().getResourceAsStream("resources/mySound.midi");


Good luck!

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #5 - Posted 2012-02-10 22:23:50 »

Quote from: Ultroman
Thing is, the MidiSystem.getSequence() takes a String

Are we talking about the same MidiSystem class? I quote the java api:
Quote
javax.sound.midi.MidiSystem

static Sequence   getSequence(File file)
           Obtains a MIDI sequence from the specified File.
static Sequence   getSequence(InputStream stream)
           Obtains a MIDI sequence from the specified input stream.
static Sequence   getSequence(URL url)
           Obtains a MIDI sequence from the specified URL.
Hi. Thanks for your reply! Sorry, that was a typo, or a brainfart or whatever. I wanted to say, that the implemented MidiPlayer-class wants a String as a location-reference. The MidiSystem.getResource used in it, wants a File. My problem, I think, is referencing to the file-path correctly, for MidiSystem to understand it. Am I right when I say that one cannot instantiate a File() using a normal String-reference when trying to access something in a .jar?

The use of new File() in 'my' MidiPlayer class should probably be exchanged for an InputStream. I tried that, but to no avail. I will try again, though. It was pretty late the last time ^^

I will keep you posted...

- Jonas
Offline H3rnst

Senior Newbie


Medals: 1



« Reply #6 - Posted 2012-02-10 22:36:12 »

Quote
1  
2  
3  
4  
5  
// This commented out one doesn't give a nullpointer exception, because the path IS right,
// but it doesn't play anything...The two underneath it, play the MIDI happily when
// executing the .jar file, but only if the .mid is in the same folder.
   
//private Sequence worldMusic = getSequence(getClass().getResourceAsStream("/snm/music/midiplayer/MainTheme.mid"));


Just to be sure I'm not reading an empty file, I'd remove the comment and change a little your getSequence(String filename) method:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
    public Sequence getSequence(String filename) {
   
    //I added this!
    File midiFile = new File(filename);
    System.out.println(midiFile.lenght());

        try {
            return MidiSystem.getSequence(midiFile);
        }
        catch (InvalidMidiDataException ex) {
            ex.printStackTrace();
            return null;
        }
        catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }




Offline sproingie

JGO Kernel


Medals: 202



« Reply #7 - Posted 2012-02-10 22:48:58 »

There's the problem.  Your resource is in a jar, but you're treating the string as a file unconditionally.   You need to use a URL that you get back from getResource(), not File.
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #8 - Posted 2012-02-10 22:53:35 »

Thanks again!

Well, now I started my hunt for glory, so I got rid of the getSequence, and started doing my referencing in the constructor, just to make sure nothing was getting in the way.

I tried this, and now it happily plays the MIDI when I run it in Eclipse, but gives a nullpointer when I try opening a built .jar with it
(well, the game crashes with black screen).

In MidiPlayer.java I load up:
fightMusic = MidiSystem.getSequence(getClass().getResourceAsStream("../midis/Fighting.mid"));

Just to be absolutely clear on the circumstances:
I have a main package called 'snm'. Inside that, is 'music', and inside that, are two packages: 'midis' and 'midiPlayer'.

So it looks like this:
snm.music.midis             <=== holds the midi-files
snm.music.midiPlayer     <==== holds the MidiPlayer.java
snm.game                  <===== holds the main game.java with the gameloop in it

- Jonas
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #9 - Posted 2012-02-10 22:54:35 »

Use a forward slash at the beginning of the String to specify the root of the jar, so as many others have repeatedly posted:
1  
MidiSystem.getSequence(getClass().getResource("/snm/music/midis/Fighting.mid");

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #10 - Posted 2012-02-10 23:05:53 »

Use a forward slash at the beginning of the String to specify the root of the jar, so as many others have repeatedly posted:
1  
2  
3  
<Magic>
MidiSystem.getSequence(getClass().getResource("/snm/music/midis/Fighting.mid");
</Magic>


THANK YOU! This was exactly what I needed. I wonder why nothing like this came up during my searches. I am sort of a noob at Java, but I feel like I'm getting a good grip on things. None of my teachers could tell me this, and I'm in 3rd semester of computer science! That's so embarrassing.

Thank you SO much, H3rnst and ra4king. This solved everything! It runs in both the IDE and from the .jar now.
I just read your great replies in the List-thread. Very interesting thoughts!

@sproingie:
I really wanted to go around that whole File-thing, and just put the stream right into that baby, and now it does. You we're all right Cheesy

- Jonas
Offline H3rnst

Senior Newbie


Medals: 1



« Reply #11 - Posted 2012-02-10 23:11:05 »

I'm glad you could fix it!  Grin

Since I'm a newbie as well, I'll ask just out of curiosity:
Use a forward slash at the beginning of the String to specify the root of the jar, so as many others have repeatedly posted:
1  
MidiSystem.getSequence(getClass().getResource("/snm/music/midis/Fighting.mid");


If he's calling this method from inside the snm.music.midiPlayer package. Shouldn't the following code have to work anyway?
1  
MidiSystem.getSequence(getClass().getResource("midis/Fighting.mid");

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #12 - Posted 2012-02-10 23:20:44 »

Well, noob to noob, I think you'd need:
1  
MidiSystem.getSequence(getClass().getResource("../midis/Fighting.mid");

In order to tell the thing to go back from midiplayer-package to music, and then proceed into midis

- Jonas
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #13 - Posted 2012-02-11 00:11:26 »

Without a forward slash at the beginning of the path, it becomes relative to the Class returned by getClass().

Offline Matthias

Senior Devvie


Medals: 3
Projects: 1


TWL - Themable Widget Library


« Reply #14 - Posted 2012-02-11 09:50:26 »

You should not use getClass() for resource loading as this will break when the class is sub classed. Use MidiPlayer.class.getResource() to have a fixed starting point.
Also make sure that the resources are in the same JAR as the class you use as a base reference otherwise you could get issues depending on how the class loaders are setup.
Offline sproingie

JGO Kernel


Medals: 202



« Reply #15 - Posted 2012-02-11 19:11:49 »

You should not use getClass() for resource loading as this will break when the class is sub classed. Use MidiPlayer.class.getResource() to have a fixed starting point.
Also make sure that the resources are in the same JAR as the class you use as a base reference otherwise you could get issues depending on how the class loaders are setup.

It will only "break" for relative paths and subclasses in a different package, in that it will be relative to the subclass in the new subclass, but you could also call that proper behavior.   You should always be using absolute resource paths anyway unless you specifically want some resource co-located with the class.

Separate jars are not a problem -- you only get separate classloaders if you're writing web apps or OSGi or if you grabbed the boot classloader for some crazy reason (like calling getClassLoader on any java.lang.* class)
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (33 views)
2014-12-15 09:26:44

Mr.CodeIt (23 views)
2014-12-14 19:50:38

BurntPizza (51 views)
2014-12-09 22:41:13

BurntPizza (84 views)
2014-12-08 04:46:31

JscottyBieshaar (45 views)
2014-12-05 12:39:02

SHC (59 views)
2014-12-03 16:27:13

CopyableCougar4 (59 views)
2014-11-29 21:32:03

toopeicgaming1999 (123 views)
2014-11-26 15:22:04

toopeicgaming1999 (114 views)
2014-11-26 15:20:36

toopeicgaming1999 (32 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!