here are all codes.
note that in my own code, i didn't write comments, these comments were added in about 2 hours as quick explanation.
these posted codes are a bit different from my own codes, as my codes have dependencies which other people don't have, but they should work with little modification.
and i am not a professional programmer, so some codes can be stupid.
JAVA SIDE
// NOTE: this is prototype and lacks error checking etc.
// sck=socket buf=buffer snd=sound grp=group
// upd=update serv=server fn=function
// short2string is a function that converts a short value to 2 characters,
// store in 2nd arg, 3rd arg is position offset
import java.io.*; import java.net.Socket;
public class Snd {
static byte fps; // used for timing, in this case framerate is constant
static float[] updResult=new float[2]; // pass data from game to this module
static byte[] outBuf=new byte[8]; // pass data from this module to socket
static Socket sck;
static boolean servOn=true, muteSnd;
static int servPort, maxVol=64000, maxPitch=64000;
static OutputStream outStream;
static String servFolder, sndFolder;
static SndType[] sndType;
static SndChannel[] sndChannel=new SndChannel[512];
static SndGrp bettyGrp; // example sound group
static short[] sndUpdFn={0,1,2};
static short[] sndTypeID={0,1,2};
static short[] sndGrpUpdFn={0,1,2};
static short[][] sndGrpItem={{0},{1},{2}};
static byte exampleFn1(SndChannel a) { updResult[0]=0f; return 1; }
static byte exampleFn2(SndChannel a) { updResult[0]=0.9f; return 1; }
static byte exampleFn3(SndChannel a) { updResult[0]=0.6f; updResult[1]=0.8f; return 2; }
interface GrpEnable { boolean enable(SndGrp a); }
interface UpdChannel { byte upd(SndChannel a); }
static GrpEnable[] grpEnable=new GrpEnable[] {
new GrpEnable() { public boolean enable(SndGrp a) { return true; } },
new GrpEnable() { public boolean enable(SndGrp a) { return true; } },
new GrpEnable() { public boolean enable(SndGrp a) { return true; } },
};
static UpdChannel[] updChannel=new UpdChannel[] {
new UpdChannel() { public byte upd(SndChannel a) { return exampleFn1(a); } },
new UpdChannel() { public byte upd(SndChannel a) { return exampleFn2(a); } },
new UpdChannel() { public byte upd(SndChannel a) { return exampleFn3(a); } },
};
static void shutdown() { if (!servOn) return;
try { outStream.close(); sck.close(); }
catch (Exception e) {} }
static void init() {
servFolder="sndServFolder/";
sndFolder="sounds/";
fps=30;
try { Runtime.getRuntime().exec(servFolder+"pysound.exe",null,new File(servFolder)); }
// in case python server app is not compiled as exe,
// run "python.exe folder/filename.py"
catch (Exception e) { servOn=false; return; }
readCFG();
readSndType();
if (!servOn) return;
for (byte m=50;m>0;m--) {
try {
Thread.sleep(300);
sck=new Socket("localhost",servPort); // only local machine, as example
outStream=sck.getOutputStream();
m=0; }
catch (Exception e) { if (m>1) continue; servOn=false; return; } }
outBuf[0]=-128; // one byte 0x80
bettyGrp=new SndGrp(0,null);
}
// setting.txt specifies socket port, same as python side.
// must be 5 digits in this simple function.
static void readCFG() {
short e=1024;
char[] u=new char[e];
int i;
String s,w="";
try { FileReader r=new FileReader(servFolder+"setting.txt"); r.read(u,0,e); r.close(); }
catch (Exception x) { servOn=false; return; }
s=new String(u); i=s.indexOf("PORT=");
if (i<0) { servOn=false; return; }
for (byte q=0;q<5;q++) { char h=u[5+i+q]; if ((h>47)&&(h<58)) w+=h; }
try { servPort=Integer.parseInt(w); }
catch (Exception x) { servOn=false; return; }
}
// an example: provide a binary file that stores sound type attributes
// the binary file also used by python side
static void readSndType() {
int h; byte[] u,q=new byte[8];
try { FileInputStream d=new FileInputStream(sndFolder+"snd.dat");
h=d.available(); u=new byte[h]; d.read(u); d.close(); }
catch (Exception x) { servOn=false; return; }
sndType=new SndType[h/8];
for (h=0;h<sndType.length;h++)
{ System.arraycopy(u,h*8,q,0,8); sndType[h]=new SndType(h,q); }
}
static void upd() { if (gamePaused||muteSnd||(!servOn)) return; bettyGrp.upd(); }
static void pause() { if (!servOn) return;
send(20);
SndChannel s;
for (short z=0;z<sndChannel.length;z++)
{ s=sndChannel[z]; if ((s!=null)&&s.playing) { s.playing=false; s.endFrm=0; } }
}
static void send(int a) { outBuf[1]=(byte)a;
try { outStream.write(outBuf); } catch (Exception i) {} }
static void delChannel(int a) { if (sndChannel[a]==null) return;
sndChannel[a].play(0f,0f); sndChannel[a]=null; }
static int findEmptyChannel() {
for (short n=0;n<sndChannel.length;n++)
{ if (sndChannel[n]==null) return n; } return -1; }
static class SndType { int typeID; float duration; boolean loop, single;
public SndType(int a,byte[] b) { typeID=a;
duration=b[3]*0.2f; single=(b[5]>0); loop=(b[3]==0); } }
static class SndChannel { boolean playing; SndType sndType;
byte[] chID=new byte[2]; short updFnID,channelID,servGrp; Object hostObj;
float vol=1f,pitch=1f; long endFrm;
// endFrm is frameID for timing in case framerate is constant
// servGrp means sound group in server, not used
// constructor: this sound channel is store in java and sent to python side
public SndChannel(SndType a,int b,int c,short d,Object e)
{ sndType=a; channelID=(short)b; servGrp=(short)c; updFnID=d; hostObj=e;
short2string_unsigned(b,chID,0); outBuf[4]=chID[0]; outBuf[5]=chID[1];
short2string_unsigned(a.typeID,outBuf,2);
short2string(c,outBuf,6); send(80); }
void play(float a,float b) {
if (a>0) { boolean s=false;
if (Math.abs(a-vol)>0.003f) { s=true; vol=a; }
if ((b>0)&&(Math.abs(b-pitch)>0.003f)) { s=true; pitch=b; }
if ((!playing)||(!sndType.loop)) { s=true; playing=true; }
if (!s) return;
short2string_unsigned(Math.min(maxVol,(int)(vol*16000)),outBuf,4);
short2string_unsigned(Math.min(maxPitch,(int)(pitch*16000)),outBuf,6); }
else if (!playing) return;
else { outBuf[4]=0; outBuf[5]=0; playing=false; endFrm=0; }
outBuf[2]=chID[0]; outBuf[3]=chID[1]; send(81); }
void upd() { if (updFnID<0) return;
byte r=updChannel[updFnID].upd(this);
if (!sndType.loop)
{ if (updResult[0]!=0) { endFrm=currentFrameID+(int)(sndType.duration*fps);
if (r==1) play(updResult[0],0);
else play(updResult[0],updResult[1]); } }
else if (updResult[0]!=0)
{ if (r==1) play(updResult[0],0);
else play(updResult[0],updResult[1]); }
else if (playing) play(0f,0f); }
}
static class SndGrp { boolean on; SndChannel[] item;
short grpTypeID,updFnID; Object hostObj;
public SndGrp(int a,Object b) { grpTypeID=(short)a; hostObj=b; short x;
updFnID=sndGrpUpdFn[a];
short[] k=sndGrpItem[a];
item=new SndChannel[k.length];
for (byte m=0;m<k.length;m++)
{ x=k[m]; int i=findEmptyChannel();
item[m]=new SndChannel(sndType[sndTypeID[x]],i,-1,sndUpdFn[x],b);
sndChannel[i]=item[m]; }
}
void upd() { boolean v=on;
if (updFnID>=0) on=grpEnable[updFnID].enable(this);
if (on) { for (byte j=0;j<item.length;j++) item[j].upd(); }
else if (v) { for (byte j=0;j<item.length;j++) item[j].play(0f,0f); }
}
}
}
PYTHON SIDE
import os,sys,time,socket; import pyglet.media
r=os.getcwd(); folder=[r+'\\',''];
folder[1]=folder[0][:-14]+'data\\snd\\'
# folder[0] is sound program folder , folder[1] is sound data folder, modify to suit your case
sndFileType=('.wav','.ogg','.mp3')
sndEOS=('pause','loop')
sndType=[]
sndChannel=[0 for r in range(512)]
sndGrp=[0 for r in range(256)]
netFn=[0 for r in range(256)]
def f020(a):
for h in xrange(len(sndChannel)):
o=sndChannel[h]
if o and o.playing:
o.player.pause(); o.playing=0
if o.sndType.duration: o.player.seek(0)
def f032(a): setGrp(string2short_unsigned(a[2:4]))
def f033(a):
k=sndGrp[string2short_unsigned(a[2:4])]
if k: k.play()
def f034(a):
k=sndGrp[string2short_unsigned(a[2:4])]
if k: k.pause()
def f080(a):
setChannel(string2short_unsigned(a[2:4]),string2short_unsigned(a[4:6]),string2short(a[6:8]))
def f081(a):
j=sndChannel[string2short_unsigned(a[2:4])]
if j: j.play(string2short_unsigned(a[4:6])*0.0000625,string2short_unsigned(a[6:8])*0.0000625)
# 0.0000625=1/16000, max 64000
def string2short_unsigned(a): return ord(a[0])+ord(a[1])*256
def string2short(a): b=ord(a[0])+ord(a[1])*256; return b-65536*bool(b>32767)
def setGrp(a):
g=sndGrp[a]; sndGrp[a]=SNDGRP(a)
if g:
for m in xrange(g.qty): sndChannel[g.item[m].channelID]=0
# arg: typeID, channelID, grpID
# sound group in server is not used yet
def setChannel(a,b,c):
n=sndChannel[b]; e=sndType[a]; sndChannel[b]=0
if n and n.grpID+1: sndGrp[n.grpID].subItem(n)
if e.src==0: e.loadSrc()
if e.src<0: return
sndChannel[b]=SNDCHANNEL(e,b)
if c+1 and sndGrp[c]: sndGrp[c].addItem(sndChannel[b])
# in example, all wav files are named like "00000000.wav" "00000001.wav" etc.
# sound type definition binary file format:
# each 8 bytes define a sound type, where:
# byte 0: file extention name type - wav ogg mp3
# byte 1/2: volume multiplier / pitch multiplier
# byte 3: sound duration, should be longer than sound file
# byte 4: preload or not
# byte 5: only allow single instance
class SNDTYPE(object):
def __init__(s,a,b):
s.typeID=a; s.fileName=('0'*8+str(a))[-8:]+sndFileType[ord(b[0])]; s.src=0
s.volMul=ord(b[1])*0.01; s.pitchMul=ord(b[2])*0.01; s.duration=ord(b[3])*0.2
s.preload=bool(ord(b[4])); s.single=bool(ord(b[5])); s.loop=bool(s.duration==0)
if s.preload: s.loadSrc()
def loadSrc(s):
if s.src: return
try: s.src=pyglet.media.load(folder[1]+s.fileName,streaming=False)
except (IOError,MediaException): s.src=-1; return
class SNDCHANNEL(object):
def __init__(s,a,b):
s.sndType=a; p=s.player=pyglet.media.Player(); p.queue(a.src); p.eos_action=sndEOS[a.loop]
s.pitchMul=p.pitch=a.pitchMul; s.volMul=p.volume=a.volMul; s.playing=s.tmpStop=0; s.vol=s.pitch=1
s.grpID=-1; s.channelID=b
def play(s,a,b):
if a>0:
if a-s.vol: s.player.volume=a*s.volMul; s.vol=a
if b and b-s.pitch: s.player.pitch=b*s.pitchMul; s.pitch=b
if s.playing-1: s.player.play(); s.playing=1
elif s.sndType.duration: s.player.seek(0); s.player.play()
elif s.playing:
s.player.pause(); s.playing=0
if s.sndType.duration: s.player.seek(0)
class SNDGRP(object):
def __init__(s,a): s.qty=0; s.item=[]; s.grpID=a
def addItem(s,a): s.item.append(a); s.qty+=1; a.grpID=s.grpID
def subItem(s,a): s.item.remove(a); s.qty-=1; a.grpID=-1
def play(s):
for h in xrange(s.qty):
i=s.item[h]
if i.tmpStop: i.player.play(); i.tmpStop=0; i.playing=1
def pause(s):
for h in xrange(s.qty):
i=s.item[h]
if i.playing: i.player.pause(); i.tmpStop=i.sndType.loop; i.playing=0
def initSnd():
try: w=file(folder[1]+'snd.dat',mode='rb'); r=w.read(); w.close()
except IOError: sys.exit()
for y in xrange(len(r)/8): sndType.append(SNDTYPE(y,r[y*8:y*8+8]))
for y in xrange(256):
try: netFn[y]=eval('f'+('000'+str(y))[-3:])
except NameError: continue
class CFG(object):
def __init__(s):
try:
i=file(folder[0]+'setting.txt'); g=i.read(); i.close()
p,r=g.find('PORT='),g.find('ACCEPT_REMOTE=')
if p<0 or r<0: sys.exit()
s.servPort,s.acceptRemote=int(g[p+5:p+10]),int(g[r+14:r+15])
except (IOError,ValueError): sys.exit()
class SERVERSOCKET(object):
def __init__(s,a):
s.port,s.remote=a.servPort,a.acceptRemote; s.running=1
k=s.sck=socket.socket(socket.AF_INET, socket.SOCK_STREAM); k.bind(('localhost'*(1-s.remote),s.port))
k.listen(1); k.setblocking(1); s.conn,s.connAddr=k.accept()
def shutdown(s): s.conn.close(); s.sck.close(); s.running=0
def run(s):
while s.running:
try:
n=s.conn.recv(8)
if not n: s.shutdown(); break
if n[0]=='\x80':
u=ord(n[1])
if netFn[u]: netFn[u](n)
except socket.error: s.shutdown()
cfg=CFG(); initSnd(); servSck=SERVERSOCKET(cfg); servSck.run()