mirror of
https://github.com/badvision/lawless-legends.git
synced 2025-02-27 20:30:11 +00:00
Improved song conversion and playback.
This commit is contained in:
parent
c819c30b34
commit
faa5270a0c
@ -33,14 +33,15 @@ for msg in mid:
|
||||
onote = int(msg.note % 12)
|
||||
lrchan = int(msg.channel & 1)
|
||||
vol = int(msg.velocity >> 3)
|
||||
print('type=%s onote=%d lrchan=%d vol=%d' % (msg.type, onote, lrchan, vol))
|
||||
print('time=%.4f type=%s octave=%d onote=%d lrchan=%d vol=%d' % (msg.time, msg.type, octave, onote, lrchan, vol))
|
||||
if msg.velocity > 0 and vol == 0:
|
||||
vol = 1
|
||||
if msg.type == 'note_off':
|
||||
vol = 0
|
||||
if octave < 0:
|
||||
octave = 0
|
||||
totaltime += deltatime
|
||||
totaltime += eventtime
|
||||
print('totaltime=%.4f scaled=%.4f' % (totaltime, totaltime*16/16.24))
|
||||
if msg.channel == 9 or msg.channel == extperchan:
|
||||
#
|
||||
# Percussion
|
||||
|
@ -23,6 +23,7 @@ import java.util.Calendar
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.LinkedHashMap
|
||||
import java.security.MessageDigest
|
||||
import javax.sound.midi.MetaMessage;
|
||||
import javax.sound.midi.MidiEvent;
|
||||
import javax.sound.midi.MidiMessage;
|
||||
import javax.sound.midi.MidiSystem;
|
||||
@ -1188,67 +1189,37 @@ class A2PackPartitions
|
||||
final int NOTE_OFF = 0x80;
|
||||
|
||||
Sequence sequence = MidiSystem.getSequence(midiFile)
|
||||
//System.out.format("Sequence: div=%.2f PPQ=%.2f res=%d\n", sequence.divisionType,
|
||||
// sequence.PPQ, sequence.resolution)
|
||||
def outBuf = ByteBuffer.allocate(50000)
|
||||
|
||||
int extperchan = 9 // this basically means no extra percussion channel, since perc is 9 anyway
|
||||
|
||||
int trackNumber = 0
|
||||
def notesByTime = [:]
|
||||
for (Track track : sequence.getTracks()) {
|
||||
trackNumber++
|
||||
//System.out.println("Track " + trackNumber + ": size = " + track.size())
|
||||
int totaltime = 0
|
||||
for (int i=0; i < track.size(); i++) {
|
||||
MidiEvent event = track.get(i)
|
||||
//System.out.print("@" + event.getTick() + " ")
|
||||
MidiMessage message = event.getMessage()
|
||||
if (message instanceof ShortMessage) {
|
||||
ShortMessage sm = (ShortMessage) message
|
||||
//System.out.print("Channel: " + sm.getChannel() + " ")
|
||||
if (sm.getCommand() == NOTE_ON || sm.getCommand() == NOTE_OFF) {
|
||||
int time = event.getTick() * 16 / 200
|
||||
int deltatime = time - totaltime
|
||||
int key = sm.getData1()
|
||||
int octave = (key / 12)-1
|
||||
int onote = key % 12
|
||||
int lrchan = sm.getChannel() & 1
|
||||
int velocity = sm.getData2()
|
||||
int vol = velocity >> 3
|
||||
totaltime = time
|
||||
//System.out.println(((sm.getCommand() == NOTE_ON) ? "on" : "off") +
|
||||
// "onote/oct=" + onote + "/" + octave +
|
||||
// " channel=" + sm.getChannel() + " lrchan=" + lrchan +
|
||||
// " key=" + key + " velocity=" + velocity + " vol=" + vol)
|
||||
if (velocity > 0 && vol == 0)
|
||||
vol = 1
|
||||
if (sm.getCommand() == NOTE_OFF)
|
||||
vol = 0
|
||||
if (octave < 0)
|
||||
octave = 0
|
||||
if (sm.getChannel() == 9 || sm.getChannel() == extperchan)
|
||||
{
|
||||
// Percussion
|
||||
if (vol > 0) {
|
||||
outBuf.put((byte)deltatime)
|
||||
outBuf.put((byte)(note ^ 0x40))
|
||||
outBuf.put((byte)((lrchan << 7) | vol))
|
||||
outBuf.put((byte)(msg.channel + 1))
|
||||
outBuf.put((byte)vol)
|
||||
if (extperchan == 9) { // Play percussion on both channels if no extended percussion
|
||||
outBuf.put((byte)0)
|
||||
outBuf.put((byte)(note ^ 0x40))
|
||||
outBuf.put((byte)vol) // omits channel
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Note
|
||||
outBuf.put((byte)deltatime)
|
||||
outBuf.put((byte)(0x80 | (octave << 4) | onote))
|
||||
outBuf.put((byte)((lrchan << 7) | vol))
|
||||
}
|
||||
|
||||
} else {
|
||||
//System.out.println("Command:" + sm.getCommand())
|
||||
int tickTrack = (event.tick << 8) | (trackNumber)
|
||||
if (!notesByTime.containsKey(tickTrack))
|
||||
notesByTime[tickTrack] = []
|
||||
notesByTime[tickTrack] << message
|
||||
} else if (message instanceof MetaMessage) {
|
||||
if (message.getType() == 0x51) {
|
||||
int usecPerBeat = ((message.getData()[0] & 0xFF) << 16) |
|
||||
((message.getData()[1] & 0xFF) << 8) |
|
||||
(message.getData()[2] & 0xFF)
|
||||
//System.out.println("Tempo message: " + usecPerBeat)
|
||||
}
|
||||
else if (message.getType() == 127) {
|
||||
//System.out.println("Meta message: type=127 data=...")
|
||||
}
|
||||
else {
|
||||
//System.out.println("Meta message: type=" + message.getType() + " data=" + message.getData())
|
||||
}
|
||||
} else {
|
||||
//System.out.println("Other message: " + message.getClass())
|
||||
@ -1256,6 +1227,64 @@ class A2PackPartitions
|
||||
}
|
||||
}
|
||||
|
||||
int totaltime = 0
|
||||
notesByTime.keySet().sort().each { int tickTrack ->
|
||||
int tick = tickTrack >> 8
|
||||
trackNumber = tickTrack & 0xFF
|
||||
notesByTime[tickTrack].each { ShortMessage sm ->
|
||||
//System.out.print("tick=" + tick + " track=" + trackNumber + " channel=" + sm.getChannel() + " ")
|
||||
if (sm.getCommand() == NOTE_ON || sm.getCommand() == NOTE_OFF) {
|
||||
int time = tick * 120.0 / sequence.resolution * 16 / 203
|
||||
int deltatime = time - totaltime
|
||||
int key = sm.getData1()
|
||||
int octave = (key / 12)-1
|
||||
int onote = key % 12
|
||||
int lrchan = sm.getChannel() & 1
|
||||
int velocity = sm.getData2()
|
||||
int vol = velocity >> 3
|
||||
totaltime = time
|
||||
//System.out.println(((sm.getCommand() == NOTE_ON) ? "on" : "off") +
|
||||
// " time=" + time +
|
||||
// " onote/oct=" + onote + "/" + octave +
|
||||
// " channel=" + sm.getChannel() + " lrchan=" + lrchan +
|
||||
// " key=" + key + " velocity=" + velocity + " vol=" + vol)
|
||||
if (velocity > 0 && vol == 0)
|
||||
vol = 1
|
||||
if (sm.getCommand() == NOTE_OFF)
|
||||
vol = 0
|
||||
if (octave < 0)
|
||||
octave = 0
|
||||
if (vol > 0)
|
||||
vol = (vol >> 2) + 12
|
||||
if (sm.getChannel() == 9 || sm.getChannel() == extperchan)
|
||||
{
|
||||
// Percussion
|
||||
if (vol > 0) {
|
||||
outBuf.put((byte)deltatime)
|
||||
outBuf.put((byte)(note ^ 0x40))
|
||||
outBuf.put((byte)((lrchan << 7) | vol))
|
||||
outBuf.put((byte)(msg.channel + 1))
|
||||
outBuf.put((byte)vol)
|
||||
if (extperchan == 9) { // Play percussion on both channels if no extended percussion
|
||||
outBuf.put((byte)0)
|
||||
outBuf.put((byte)(note ^ 0x40))
|
||||
outBuf.put((byte)vol) // omits channel
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Note
|
||||
outBuf.put((byte)deltatime)
|
||||
outBuf.put((byte)(0x80 | (octave << 4) | onote))
|
||||
outBuf.put((byte)((lrchan << 7) | vol))
|
||||
}
|
||||
} else {
|
||||
//System.out.println("Command:" + sm.getCommand())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compress and store the resulting buffer
|
||||
def num = songs.size() + 1
|
||||
songs[midiFile.name] = [num:num, buf:compress(unwrapByteBuffer(outBuf))]
|
||||
addResourceDep("map", "<root>", "song", midiFile.name)
|
||||
@ -2472,7 +2501,7 @@ class A2PackPartitions
|
||||
packGlobalTileSet(dataIn)
|
||||
|
||||
// Play around with music
|
||||
def midiFile = new File(xmlFile.getParentFile(), "song.mid")
|
||||
def midiFile = new File(xmlFile.getAbsoluteFile().getParentFile(), "song.mid")
|
||||
if (midiFile.exists())
|
||||
packSong(midiFile)
|
||||
|
||||
|
@ -16,7 +16,7 @@ const rndseed = $004E
|
||||
const LSB = 0
|
||||
const MSB = 1
|
||||
const MB_ARPEGGIO = 4 // In 16ths of a second
|
||||
const MAX_MBCH_NOTES = 9
|
||||
const MAX_MBCH_NOTES = 3
|
||||
const SPKR_ARPEGGIO = 2 // In 16ths of a second
|
||||
const DUR16TH = 8
|
||||
const MAX_SPKR_NOTES = 4
|
||||
@ -95,6 +95,7 @@ word seqTrack, seqEvent, seqTime, eventTime, updateTime
|
||||
byte numNotes, seqRepeat
|
||||
byte indexA[2], indexB[2], indexC[2]
|
||||
byte noteA[2], noteB[2], noteC[2]
|
||||
byte volA[2], volB[2], volC[2]
|
||||
word notes1[MAX_MBCH_NOTES], notes2[MAX_MBCH_NOTES]
|
||||
word notes[2] = @notes1, @notes2
|
||||
word periods1[MAX_MBCH_NOTES], periods2[MAX_MBCH_NOTES]
|
||||
@ -513,6 +514,9 @@ def mbSequence(yield, func)#0
|
||||
noteA[0] = 0; noteA[1] = 0
|
||||
noteB[0] = 0; noteB[1] = 0
|
||||
noteC[0] = 0; noteC[1] = 0
|
||||
volA[0] = 0; volA[1] = 0
|
||||
volB[0] = 0; volB[1] = 0
|
||||
volC[0] = 0; volC[1] = 0
|
||||
//
|
||||
// Get the PSGs ready
|
||||
//
|
||||
@ -639,6 +643,7 @@ def mbSequence(yield, func)#0
|
||||
if n.LSB <> noteA[channel]
|
||||
psgWriteTone(mbVIAs[channel], AFREQ, periods[channel, i], n.MSB)
|
||||
noteA[channel] = n.LSB
|
||||
volA[channel] = n.MSB | $80
|
||||
indexA[channel] = i
|
||||
fin
|
||||
//
|
||||
@ -655,6 +660,7 @@ def mbSequence(yield, func)#0
|
||||
if n.LSB <> noteB[channel]
|
||||
psgWriteTone(mbVIAs[channel], BFREQ, periods[channel, i], n.MSB)
|
||||
noteB[channel] = n.LSB
|
||||
volB[channel] = n.MSB | $80
|
||||
indexB[channel] = i
|
||||
fin
|
||||
//
|
||||
@ -672,6 +678,7 @@ def mbSequence(yield, func)#0
|
||||
psgWrite(mbVIAs[channel], MIXER, $38) // Tone on C, B, A
|
||||
psgWriteTone(mbVIAs[channel], CFREQ, periods[channel, i], n.MSB)
|
||||
noteC[channel] = n.LSB
|
||||
volC[channel] = n.MSB | $80
|
||||
indexC[channel] = i
|
||||
fin
|
||||
next
|
||||
@ -681,9 +688,45 @@ def mbSequence(yield, func)#0
|
||||
// Increment time tick
|
||||
//
|
||||
seqTime++
|
||||
while !(mbVIA1->IFR & $40) // Wait for T1 interrupt
|
||||
period = 0
|
||||
repeat
|
||||
if a2keypressed(); quit = TRUE; break; fin
|
||||
loop
|
||||
period++
|
||||
if (period & 63) == 1
|
||||
for channel = 0 to 1
|
||||
if volA[channel] & $80
|
||||
volA[channel] = volA[channel] & $7F
|
||||
elsif volA[channel]
|
||||
volA[channel]--
|
||||
psgWrite(mbVIAs[channel], AENVAMP, volA[channel])
|
||||
if volA[channel] == 0
|
||||
noteA[channel] = 0
|
||||
numNotes--
|
||||
fin
|
||||
fin
|
||||
if volB[channel] & $80
|
||||
volB[channel] = volB[channel] & $7F
|
||||
elsif volB[channel]
|
||||
volB[channel]--
|
||||
psgWrite(mbVIAs[channel], BENVAMP, volB[channel])
|
||||
if volB[channel] == 0
|
||||
noteB[channel] = 0
|
||||
numNotes--
|
||||
fin
|
||||
fin
|
||||
if volC[channel] & $80
|
||||
volC[channel] = volC[channel] & $7F
|
||||
elsif volC[channel]
|
||||
volC[channel]--
|
||||
psgWrite(mbVIAs[channel], CENVAMP, volC[channel])
|
||||
if volC[channel] == 0
|
||||
noteC[channel] = 0
|
||||
numNotes--
|
||||
fin
|
||||
fin
|
||||
next
|
||||
fin
|
||||
until mbVIA1->IFR & $40 // Wait for T1 interrupt
|
||||
mbVIA1->IFR = $40 // Clear interrupt
|
||||
if yieldTime <= seqTime; func()#0; yieldTime = seqTime + yield; fin
|
||||
until quit
|
||||
|
Loading…
x
Reference in New Issue
Block a user