Page 1 of 2
SPC player in java help?
Posted: Sat Aug 21, 2010 7:21 pm
by counterp
I'm trying to play spc files using java, and I stumbled across this thread:
http://nesdev.com/bbs/viewtopic.php?t=6222
In it, the user blargg posted his java music player that had the capability of playing spc files.
However I am not able to just rip out the spc playing part, can anyone help me please?
Posted: Sun Aug 22, 2010 4:42 am
by Zoigl
a detailed question would be much better

Posted: Mon Aug 23, 2010 3:58 am
by counterp
ok, can someone explain the protocol for playing spc files in java, and post the example code for playing a single spc file?
please and thanks
Posted: Mon Aug 23, 2010 9:14 am
by Zoigl
ok nice
are you developer or you do it just for fun?
I also checked the code and (sorry blargg) the code is not as easy readable for me as java developer.
You should try to analyze the project and "extract" the SPC code. If you got questions on some java related parts, i could help you but if you got questions related to the emulation-core code, you should directly ask blargg.
If i had time, i would do a little redesing of the project and would release it on sourgeforge or something else - if blargg would agree. But at the moment i am working on a browser game in java.
Posted: Mon Aug 23, 2010 4:55 pm
by counterp
I'm making a mario game for fun and the original spc music files are very small so I'd like to use them.
And yeah, his code is hard to understand since it's not documented, and I can't find the spc protocol.
Posted: Mon Aug 23, 2010 10:15 pm
by mic_
Look at VGMPlayer.java, and gme.java which uses it.
Posted: Tue Aug 24, 2010 3:26 am
by blargg
I still haven't understood what this thread is about. What is the drawback of using the player as it currently is?
Posted: Tue Aug 24, 2010 5:48 am
by Banshaku
I guess the question is: I want to use the code as my music driver for my java game but I don't know how (i.e. like using famitracker driver in a nes game).
Posted: Tue Aug 24, 2010 6:38 am
by blargg
Here's the main class:
Code: Select all
class VGMPlayer implements Runnable
{
// Establishes sample rate
public VGMPlayer( int sampleRate )
// Stops playback and loads file from given URL (HTTP only).
// If it's an archive (.zip) then path specifies the file within
// the archive.
public void loadFile( String url, String path ) throws Exception
// Stops and closes current file and unloads things from memory
void closeFile() throws Exception
// Number of tracks
public int getTrackCount()
// Starts new track playing, where 0 is the first track.
// After time seconds, the track starts fading.
public void startTrack( int track, int time ) throws Exception
// Currently playing track
public int getCurrentTrack()
// Number of seconds played since last startTrack() call
public int getCurrentTime()
// Sets playback volume, where 1.0 is normal, 2.0 is twice as loud.
// Can be changed while track is playing.
public void setVolume( double v )
// Current playback volume
public double getVolume()
// Pauses if track was playing.
public void pause() throws Exception
// True if track is currently playing
public boolean isPlaying()
// Resumes playback where it was paused
public void play() throws Exception
// Stops playback and closes audio
public void stop() throws Exception
// Called periodically when a track is playing
protected void idle()
}
// example of use
VGMPlayer player = new VGMPlayer( 44100 );
// Open a file, or a file in a zip archive
player.loadFile( "file.spc", "" );
player.loadFile( "archive.zip", "file.spc" );
// Play for 4 minutes, then fade
player.startTrack( 0, 240 );
Posted: Mon Aug 30, 2010 6:26 pm
by counterp
Can't seem to get it working.
Code: Select all
java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at src.sounds.emu.SpcEmu.startTrack(SpcEmu.java:147)
at src.sounds.EmuPlayer.startTrack(VGMPlayer.java:44)
at src.sounds.SoundManager.play(SoundManager.java:42)
at src.Client.<init>(Client.java:30)
at src.Client.main(Client.java:15)
sounds.zip is zip archive holding 62 spc files. I load all of them to a byte array..
However I get an error when I call the play method.
The only file I have modified is VGMPlayer
Code: Select all
package src.sounds;
import java.io.*;
import java.util.zip.*;
import javax.sound.sampled.*;
import src.sounds.emu.*;
public class SoundManager {
private static final SoundManager instance = new SoundManager();
public static final SoundManager getInstance() {
return instance;
}
private SoundManager() {
try {
player = new VGMPlayer(44100);
ZipInputStream in = new ZipInputStream(new FileInputStream("data/sounds.zip"));
ZipEntry clip = null;
int i = 0;
byte[] buf;
while ((clip = in.getNextEntry()) != null) {
buf = new byte[(int) clip.getCompressedSize()];
in.read(buf);
sound[i++] = buf;
}
in.close();
} catch (Exception e) {
System.exit(1);
}
}
private byte[][] sound = new byte[62][];
private VGMPlayer player;
public void play(int id, int time) {
try {
player.loadFile(sound[id]);
player.startTrack(0, time);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Code: Select all
package src.sounds;
import javax.sound.sampled.*;
import java.io.*;
import src.sounds.emu.*;
/* Copyright (C) 2007-2008 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
class EmuPlayer implements Runnable {
public int getTrackCount() {
return emu.trackCount();
}
public void startTrack(int track, int time) throws Exception {
pause();
if (line != null)
line.flush();
emu.startTrack(track);
emu.setFade(time, 6);
play();
}
public int getCurrentTrack() {
return emu.currentTrack();
}
public int getCurrentTime() {
return (emu == null ? 0 : emu.currentTime());
}
public void setVolume(double v) {
volume_ = v;
if (line != null) {
FloatControl mg = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
if (mg != null)
mg.setValue((float) (Math.log( v ) / Math.log( 10.0 ) * 20.0));
}
}
public double getVolume() {
return volume_;
}
public void pause() throws Exception {
if (thread != null) {
playing_ = false;
thread.join();
thread = null;
}
}
public boolean isPlaying() {
return playing_;
}
public void play() throws Exception {
if (line == null) {
line = (SourceDataLine) AudioSystem.getLine(lineInfo);
line.open(audioFormat);
setVolume(volume_);
}
thread = new Thread(this);
playing_ = true;
thread.start();
}
public void stop() throws Exception {
pause();
if (line != null) {
line.close();
line = null;
}
}
protected void idle() { }
private int sampleRate = 0;
AudioFormat audioFormat;
DataLine.Info lineInfo;
MusicEmu emu = new SpcEmu();
Thread thread;
volatile boolean playing_;
SourceDataLine line;
double volume_ = 1.0;
public void run() {
line.start();
byte [] buf = new byte [66092];
while (playing_ && !emu.trackEnded()) {
int count = emu.play(buf, buf.length / 2);
line.write(buf, 0, count * 2);
idle();
}
playing_ = false;
line.stop();
}
}
class VGMPlayer extends EmuPlayer {
int sampleRate;
public VGMPlayer(int sampleRate) {
this.sampleRate = sampleRate;
}
public void loadFile(byte[] data) throws Exception {
stop();
int actualSampleRate = emu.setSampleRate( sampleRate );
emu.loadFile(data);
}
void closeFile() throws Exception {
stop();
}
}
Posted: Mon Aug 30, 2010 7:30 pm
by blargg
Your changes removed essential things. I don't have any Java compiler handy, but try the following class in your own code (revert back to the unmodified GME sources; I don't think you need to modify them):
Code: Select all
class SPCPlayer extends EmuPlayer
{
public void useSpcFile( byte [] data, int sampleRate ) throws Exception {
stop();
MusicEmu emu = new SpcEmu();
int actualSampleRate = emu.setSampleRate(sampleRate);
emu.loadFile(data);
setEmu(emu, actualSampleRate);
}
void closeFile() throws Exception {
stop();
setEmu(null, 0);
}
}
Posted: Tue Aug 31, 2010 6:21 am
by counterp
Hmm I changed back to original class then added your SPCPlayer class but I still get the same error.
Code: Select all
java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at src.sounds.emu.SpcEmu.startTrack(SpcEmu.java:147)
at src.sounds.EmuPlayer.startTrack(SPCPlayer.java:45)
at src.sounds.SoundManager.play(SoundManager.java:43)
at src.Client.<init>(Client.java:30)
at src.Client.main(Client.java:15)
Posted: Tue Aug 31, 2010 1:34 pm
by blargg
Have you verified that the data array you're passing to useSpcFile is a valid SPC file? If you're still having trouble, you can send me your full Java source and I'll figure out what's going on.
Posted: Tue Aug 31, 2010 6:04 pm
by counterp
Yeah I'm pretty sure it is.
Posted: Tue Aug 31, 2010 6:57 pm
by blargg
Could you check, before I spend a lot of time examining your source? Be sure the header looks right, AND please report back the file length. I'm also wondering whether the original unmodified source worked for playing a SPC. That is, where you pass it the path to the file.