Моя попытка стать разработчиком Кайрэндии

Обсуждение технических вопросов (проблемы с запуском игры, баги, глюки, баглюки)

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение Reflector » 05 май 2017, 17:21

bckpkol писал(а):Это распаковка. Да, я заметил, что в dat есть что-то похожее на палитру. А упаковка? Какой формат у заголовка?
И размеры у dat файлов разные. Нет, там нечто большее, чем кусок палитры.

Распакуй .dat моим экстрактором и увидишь что там есть еще. И еще раз повторяю, у меня нет спецификации, нет формата заголовка, я сразу писал код ничего не записывая, причем если говорить от .dat, то помимо палитры, с которой я разобрался раньше, все остальное подсмотрено в scumm, наверно именно потому так и получилось :)

Кстати, что это за метка 53 4b?

Последние две буквы от ".MSK"?

Reflector писал(а):Внешняя палитра? Уверен, что не внутренняя, запакованная в cps? Если нет, что понимается под внешней палитрой?
Странно... Взять одну палитру, скопировать из другой, затем странные магические числа 0x17 и 60.

Для k2 в pak обычно лежит палитра, иногда несколько, иногда ни одной, т.е. можно передать в качестве параметра конкретную палитру или же обойтись глобальной палитрой для игры в целом(если в хедере не указано, что используется встроенная в cps). Для k1 у меня в коде можно передавать внешнюю палитру, но используется ли она для фонов или только для анимаций я уже точно не помню...

Тут хотелось бы спросить подробнее. Так как VGA использует 6 бит на канал, 18 на цвет, палитра должна быть 576 байт.
Мне непонятно, зачем нужно брать вторую половину, разве палитра изначально не целая?

Художник рисует анимацию для конкретной локации, ему говорят, что ты можешь создать себе какую хочешь палитру, но не более такого-то размера, потому что мы туда втиснем еще часть другой палитры нужной для отрисовки той графики, которая может присутствовать на любой из локаций. А почему в одних случаях они вставляют небольшой кусочек, а в других половину палитры, я без понятия :)
Когда обнаружились проблемы с палитрами, я стал делать в досбоксе скриншоты, затем вытягивал в фотошопе палитру, сравнивал и искал закономерности. Какие нашел, так и сделал, может местами там можно сделать и проще.

ColorScale - это то же самое, что и <<2?

Типа того, но не совсем. Если сдвинуть 63 на 2 позиции влево, то получим 252, а не 255, потому я множу на (255 / 63) и округляю до ближайшего целого. Так же само сделано в досбоксе, по крайней мере когда я проверял то результаты были одинаковыми.

Вопрос не в тему. Я единственный, кому кажется, что исходники scummvm нечитаемы?
Что я понял - scummvm - это рендерер, который динамически загружает ресурсы игры. Отдельного файла на каждый тип ресурса нет, всё распихано непонятно как.

По именам примерно можно понять какие файлы за что отвечают, а в целом да, разобраться там не так и просто. В особо запущенных случаях можно самому скомпилить и выводить отладочную информацию.
Reflector
(2) Житель Милтонии
 
Сообщения: 163
Зарегистрирован: 11 сен 2010, 16:44
Любимая часть Кирандии: 2
Любимые персонажи Кирандии: Занция
Почему Вы любите Легенду о Кирандии?: Ностальгия...

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 06 май 2017, 09:04

Да, подправил, как ты сказал, Reflector. Всё же возвращает кучу прозрачных пикселей. Судя по всему, ошибка во второй команде.
http://imgur.com/nBf82Qo вот эта хрень.
Нет, похоже, в третьей, так как разница начинается с 2570-ого пиксела.
Исправил ошибку. Похоже, она не единственная.
А. Всё дело было в палитре. Добавил параметр прозрачности первого пиксела, и заработало.
Кстати, тот KyrExtractor, что у меня,
Support .pak, .apk, .vrm, .cps, .msc, .wsa, .csh, .shp, .voc, .tlk

, но не dat.
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение Reflector » 06 май 2017, 11:09

bckpkol писал(а):А. Всё дело было в палитре. Добавил параметр прозрачности первого пиксела, и заработало.

Кстати да, вспомнил, что есть две локации на которых проблема с прозрачностью: dinoa и instore. Видимо там прозрачный пиксель в палитре не первый, нужно поискать где этот параметр задается.

Кстати, тот KyrExtractor, что у меня,
Support .pak, .apk, .vrm, .cps, .msc, .wsa, .csh, .shp, .voc, .tlk, но не dat.

Держи, у тебя какой-то совсем старый, даже emc не понимает.
Support pak, apk, vrm, cps, msc, wsa, dat, csh, shp, fnt, voc, tlk, emc, tim, tre, dle

ps. Нет, похоже с прозрачностью все нормально, просто ее не нужно включать для самого фона, а только для спрайтов расположенных ниже, что для картинки с палитрой весьма проблематично. Добавил для фонов параметр -noalpha.
Reflector
(2) Житель Милтонии
 
Сообщения: 163
Зарегистрирован: 11 сен 2010, 16:44
Любимая часть Кирандии: 2
Любимые персонажи Кирандии: Занция
Почему Вы любите Легенду о Кирандии?: Ностальгия...

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 11 май 2017, 15:33

Код: Выделить всё
import random
def get_bit(depth,color):
    mask=128>>depth
    return ((bool(color[0]&mask))|(bool(color[1]&mask)<<1)|(bool(color[2]&mask)<<2))+1
class OctreeNode(object):
    def __init__(self,color):
        self.R=color[0]
        self.G=color[1]
        self.B=color[2]
        self.len=1
    def __len__(self):
        return self.len
    def add(self,color):
        self.R+=color[0]
        self.G+=color[1]
        self.B+=color[2]
        self.len+=1
    def average(self):
        return (self.R//len(self),self.G//len(self),self.B//len(self))
class OctreeIndexNode(object):
    def __init__(self,index):
        self.index=index
        self.len=1
class OctreeBranch(list):
    def __init__(self,depth):
        list.__init__(self,[None])
        self.depth=depth
        if depth<6:#8 if not VGA
            self.extend([None]*8)
            self.leaf=False
        else:
            self.leaf=True
    def add_color(self,color):
        if isinstance(self[0],OctreeNode):
            self[0].add(color)
        else:
            self[0]=OctreeNode(color)
        if not self.leaf:
#            print('not leaf',self.depth)
            index=get_bit(self.depth,color)
            if not isinstance(self[index],OctreeBranch):
                self[index]=OctreeBranch(self.depth+1)
            self[index].add_color(color)
    def add_index(self,color,palindex):
        if self.leaf:
            self[0]=OctreeIndexNode(palindex)
        else:
            index=get_bit(self.depth,color)
            if not isinstance(self[index],OctreeBranch):
                self[index]=OctreeBranch(self.depth+1)
            self[index].add_index(color)
    def go_deep(self,color):
        if self.leaf:
            return self[0].average()
        index=get_bit(self.depth,color)
        if not isinstance(self[index],OctreeBranch):
            for branch in sorted(self[1:],key=lambda x:random.random()):
                if isinstance(branch,OctreeBranch):
                    return branch.go_deep(color)
        return self[index].go_deep(color)
    def go_index(self,color):
        if self.leaf:
            return self[0].index
        index=get_bit(self.depth,color)
        if not isinstance(self[index],OctreeBranch):
            for branch in sorted(self[1:],key=lambda x:random.random()):
                if isinstance(branch,OctreeBranch):
                    return branch.go_index(color)
        return self[index].go_index(color)
    def flatten(self):
        for branch in self:
            if isinstance(branch,OctreeBranch):
                if not branch.leaf:
                    yield branch
                    for leaf in branch.flatten():
                        if isinstance(leaf,OctreeBranch):
                            yield leaf
    def leaves(self):
        for branch in self:
            if isinstance(branch,OctreeBranch):
                if branch.leaf:
                    yield branch
                else:
                    count=0
                    for leaf in branch.leaves():
                        if isinstance(leaf,OctreeBranch):
                            count+=1
                            yield leaf
                    if count==0:
                        branch.cut()
                        yield branch
    def cut(self):
        ret=0
        for i in self[1:]:
            if i!=None:
                ret+=1
        self.leaf=True
        return ret
    def fit_to_size(self,size):
#        print(self)
#        print(list(self.flatten()))
        branches=sorted(self.flatten(),key=lambda x:len(x[0]))
        leaves=sorted(self.leaves(),key=lambda x:len(x[0]),reverse=True)
#        print(branches)
        while len(leaves)>size:
            tocut=len(leaves)-size
            i=0
            while tocut>0:
                tocut-=branches[i].cut()
                i+=1
            branches=sorted(self.flatten(),key=lambda x:len(x[0]))
            leaves=sorted(self.leaves(),key=lambda x:len(x[0]),reverse=True)
#        print(sorted(self.leaves(),key=lambda x:len(x[0])))
        return [branch[0].average() for branch in leaves]

Код: Выделить всё
import sys
if len(sys.argv)!=3:
    sys.exit('usage: py toindexed_simple.py [C:\path\]image.jpg [C:\path\]indexed.png')
from PIL import Image
from octreequantizer import OctreeBranch
im = Image.open(sys.argv[1])
rgbaim=im.convert('RGBA')
width, height = im.size
imdata=rgbaim.getdata()
pixels=[(0,0,0) if pixel[3]==0 else pixel[:3] for pixel in imdata]
octreel=OctreeBranch(0)
for pixel in pixels:
    octreel.add_color(pixel)
pal=octreel.fit_to_size(255)
def alpha_index(imdata,pal,octreel):
    for pixel in imdata:
        if pixel[3]==0:
            yield 0
        else:
            yield pal.index(octreel.go_deep(pixel[:3]))+1
indexed=list(alpha_index(imdata,pal,octreel))
pala=(0,0,0,)+sum(pal,())
indexedim=Image.new('L',im.size)
indexedim.putdata(indexed)
indexedim.putpalette(pala)
indexedim.save(sys.argv[2],transparency=0)

Код: Выделить всё
import sys,time,struct
if len(sys.argv)!=3:
    sys.exit('usage: py toindexed_cps.py [C:\path\]image.jpg [C:\path\]indexed.png')
from PIL import Image
from octreequantizer import OctreeBranch
def longest(string,substring):
    word=128
    bias=128
    while True:
        if bias:
            if string[:word+bias]==substring[:word+bias]:
                word=word+bias
                bias<<=1
            elif string[:word]==substring[:word]:
                bias>>=1
            else:
                bias>>=1
                word-=bias
        else:
            return word
class RLE(object):
    def __init__(self,inp,match):
        self.start=len(inp)
        self.two=match[:2]
        self.color=match[0:1]
        self.len=0
        self.bin=self.raw(match[:63])
        if match[:3]==self.color*3:
            for color in match:
                if color==self.color[0]:
                    self.len+=1
                else:
                    break
            self.bin=self.fill(self.color,self.len)
        for x in range(self.start-1,-1,-1):
            if inp[x]==match[0]:
                y=longest(inp[x:],match)
                if y>max(len(self),2):
                    self.len=y
                    self.bin=self.copy(offset=x,pos=self.start,count=self.len)
        if len(self)<=min(len(self.bin)-1,63):
            self.bin=self.raw(match[:63])
            self.len=len(self.bin)-1
    def copy(self,offset,pos,count):
        rel=pos-offset
        if count<11:
            if rel<4096:
                return bytes([((count-3)<<4)|(rel>>8),rel&255])
        if count<64:
            return bytes([192|(count-3),offset&255,offset>>8])
        return bytes([255,count&255,count>>8,offset&255,offset>>8])
    def fill(self,color,count):
        return bytes([254,count&255,count>>8])+color
    def raw(self,data):
        return bytes([128|len(data)])+data
    def __len__(self):
        return self.len
    def lenforce(self):
        selftype=self.type()
        if selftype==5:
            return (self.bin[2]<<8)|self.bin[1]
        if selftype==4:
            return (self.bin[2]<<8)|self.bin[1]
        if selftype==3:
            return (self.bin[0]&63)+3
        if selftype==2:
            return ((self.bin[0]&112)>>4)+3
        else:
            return (self.bin[0]&63)
    def type(self):
        if self.bin[0]&128:
            if self.bin[0]&64:
                if self.bin[0]==255:#large copy, should be used if small copy won't do
                    return 5
                elif self.bin[0]==254:#fill, should be used first
                    return 4
                else:#small copy, should be used if repeat won't do
                    return 3
            else:#raw, tried after copy
                return 1
        else:#repeat, tried after fill
            return 2
    def strip(self,reqlen):
        selftype=self.type()
        if reqlen>=len(self):
            return self.bin
        elif reqlen<3:
            return bytes([128|reqlen])+self.two[:reqlen]
        elif selftype==5:
            return bytes([255,reqlen&255,reqlen>>8])+self.bin[3:]
        elif selftype==4:
            return bytes([254,reqlen&255,reqlen>>8])+self.bin[3:]
        elif selftype==3:
            return bytes([192|(reqlen-3)])+self.bin[1:]
        elif selftype==2:
            return bytes([((reqlen-3)<<4)|(self.bin[0]&15)])+self.bin[1:]
        else:
            return bytes([128|reqlen])+self.bin[1:1+reqlen]
im = Image.open(sys.argv[1])
rgbaim=im.convert('RGBA')
if im.size!=(320,200):
    print('unsupported resolution')
imdata=rgbaim.getdata()
pixels=[(0,0,0) if pixel[3]==0 else pixel[:3] for pixel in imdata]
octreel=OctreeBranch(0)
for pixel in pixels:
    octreel.add_color(pixel)
pal=octreel.fit_to_size(255)
def alpha_index(imdata,pal,octreel):
    for pixel in imdata:
        if pixel[3]==0:
            yield 0
        else:
            yield pal.index(octreel.go_deep(pixel[:3]))+1
indexed=bytes(alpha_index(imdata,pal,octreel))
pala=(b'\x00\x00\x00'+bytes((c>>2 for b in pal for c in b))+b'\x00'*768)[:768]
inpos=0
compress=b''
buffr=[]
while inpos<len(indexed):
    rle=RLE(indexed[:inpos],indexed[inpos:])
#    if rle.len!=rle.lenforce():
#        print('mismatch at',inpos,'type',rle.type(),'len',rle.len,'forcelen',rle.lenforce())
    buffr=[]
    while rle.type()==1:
        buffr.append(rle)
        inpos+=1
        rle=RLE(indexed[:inpos],indexed[inpos:])
    if len(buffr):
        compress+=buffr[0].strip(len(buffr))
    compress+=rle.bin
    inpos+=rle.len
compressionType=struct.pack('<H',4)
uncompressedSize=struct.pack('<I',64000)
paletteSize=struct.pack('<H',768)
fileSize=struct.pack('<H',8+len(compress))
with open(sys.argv[2],'wb') as f:
    f.write(fileSize)
    f.write(compressionType)
    f.write(uncompressedSize)
    f.write(paletteSize)
    f.write(pala)
    f.write(compress)

Код: Выделить всё
import sys,time,struct
if len(sys.argv)!=3:
    sys.exit('usage: py toindexed_cps_noquantize.py [C:\path\]image.jpg [C:\path\]indexed.png')
from PIL import Image
def longest(string,substring):
    word=128
    bias=128
    while True:
        if bias:
            if string[:word+bias]==substring[:word+bias]:
                word=word+bias
                bias<<=1
            elif string[:word]==substring[:word]:
                bias>>=1
            else:
                bias>>=1
                word-=bias
        else:
            return word
class RLE(object):
    def __init__(self,inp,match):
        self.start=len(inp)
        self.two=match[:2]
        self.color=match[0:1]
        self.len=0
        self.bin=self.raw(match[:63])
        if match[:3]==self.color*3:
            for color in match:
                if color==self.color[0]:
                    self.len+=1
                else:
                    break
            self.bin=self.fill(self.color,self.len)
        for x in range(self.start-1,-1,-1):
            if inp[x]==match[0]:
                y=longest(inp[x:],match)
                if y>max(len(self),2):
                    self.len=y
                    self.bin=self.copy(offset=x,pos=self.start,count=self.len)
        if len(self)<=min(len(self.bin)-1,63):
            self.bin=self.raw(match[:63])
            self.len=len(self.bin)-1
    def copy(self,offset,pos,count):
        rel=pos-offset
        if count<11:
            if rel<4096:
                return bytes([((count-3)<<4)|(rel>>8),rel&255])
        if count<64:
            return bytes([192|(count-3),offset&255,offset>>8])
        return bytes([255,count&255,count>>8,offset&255,offset>>8])
    def fill(self,color,count):
        return bytes([254,count&255,count>>8])+color
    def raw(self,data):
        return bytes([128|len(data)])+data
    def __len__(self):
        return self.len
    def lenforce(self):
        selftype=self.type()
        if selftype==5:
            return (self.bin[2]<<8)|self.bin[1]
        if selftype==4:
            return (self.bin[2]<<8)|self.bin[1]
        if selftype==3:
            return (self.bin[0]&63)+3
        if selftype==2:
            return ((self.bin[0]&112)>>4)+3
        else:
            return (self.bin[0]&63)
    def type(self):
        if self.bin[0]&128:
            if self.bin[0]&64:
                if self.bin[0]==255:#large copy, should be used if small copy won't do
                    return 5
                elif self.bin[0]==254:#fill, should be used first
                    return 4
                else:#small copy, should be used if repeat won't do
                    return 3
            else:#raw, tried after copy
                return 1
        else:#repeat, tried after fill
            return 2
    def strip(self,reqlen):
        selftype=self.type()
        if reqlen>=len(self):
            return self.bin
        elif reqlen<3:
            return bytes([128|reqlen])+self.two[:reqlen]
        elif selftype==5:
            return bytes([255,reqlen&255,reqlen>>8])+self.bin[3:]
        elif selftype==4:
            return bytes([254,reqlen&255,reqlen>>8])+self.bin[3:]
        elif selftype==3:
            return bytes([192|(reqlen-3)])+self.bin[1:]
        elif selftype==2:
            return bytes([((reqlen-3)<<4)|(self.bin[0]&15)])+self.bin[1:]
        else:
            return bytes([128|reqlen])+self.bin[1:1+reqlen]
im = Image.open(sys.argv[1])
if im.mode!='P':
    sys.exit('no palette to strip')
if im.size!=(320,200):
    print('unsupported resolution')
indexed=bytes(im.getdata())
inpos=0
compress=b''
buffr=[]
while inpos<len(indexed):
    rle=RLE(indexed[:inpos],indexed[inpos:])
#    if rle.len!=rle.lenforce():
#        print('mismatch at',inpos,'type',rle.type(),'len',rle.len,'forcelen',rle.lenforce())
    buffr=[]
    while rle.type()==1:
        buffr.append(rle)
        inpos+=1
        rle=RLE(indexed[:inpos],indexed[inpos:])
    if len(buffr):
        compress+=buffr[0].strip(len(buffr))
    compress+=rle.bin
    inpos+=rle.len
compressionType=struct.pack('<H',4)
uncompressedSize=struct.pack('<I',64000)
paletteSize=struct.pack('<H',0)
fileSize=struct.pack('<H',8+len(compress))
with open(sys.argv[2],'wb') as f:
    f.write(fileSize)
    f.write(compressionType)
    f.write(uncompressedSize)
    f.write(paletteSize)
    f.write(compress)

Последний - конвертер в cps, сохраняющий индекс и удаляющий палитру. Жутко медленный, как ни старался. Результат на килобайт больше оригинала.
И куча кода - для отладки.
Добавлено: если запускать на pypy3, скорость приличная.
Последний раз редактировалось bckpkol 12 май 2017, 19:54, всего редактировалось 1 раз.
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 12 май 2017, 17:18

Код: Выделить всё
import sys,struct
from midplay import MidiFile
from writemidtype2 import compress_notation
if __name__=='__main__':
    if len(sys.argv)!=3:
        sys.exit('usage: py writemidtype1.py [C:\path]filename.mid [C:\path]filename')
    midi=MidiFile(sys.argv[1])
    for num,track in enumerate(midi):
        channeltracks=[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
        for msg in track:
            if hasattr(msg,'channel'):
                channeltracks[msg.channel+1].append(msg)
            else:
                channeltracks[0].append(msg)
        while [] in channeltracks:
            del channeltracks[channeltracks.index([])]
        raw=b'MThd\x00\x00\x00\x06'+struct.pack('>H',1)+struct.pack('>H',len(channeltracks))+struct.pack('>h',midi.TPQN)
        for channel in channeltracks:
            rawTrack=compress_notation(channel)
            raw+=b'MTrk'+struct.pack('>I',len(rawTrack))+rawTrack
        f=open(sys.argv[2]+'.'+str(num).rjust(3,'0')+'.mid','wb')
        f.write(raw)
        f.close()

Да, мой midi конвертер делает рабочие midi файлы. Но каноничный midi формат не поддерживался. И этот код добавляет его поддержку.
Я это к чему? Я решил не изобретать велосипед и использовать abc-нотацию, и abc-конвертер требует каноничный формат.
Код: Выделить всё
X: 1
T: from kyra1a.002.mid
M: 4/4
L: 1/8
Q:1/4=120
K:C % 0 sharps
V:1
%%MIDI program 8
e''f'' a''2 x3x/2a''/2-| \
[a''/2f''/2-]f''/2e''2x3 x/2e''f''/2-| \
f''/2a''2x3x/2 a''f''/2-[f''/2e''/2-]| \
e''3/2x3x/2f'' ^g''b''-|
b''x3 x/2b''^g''f''3/2-| \
f''/2x3/2 f''2 d''^c''/2-[d''/2-^c''/2] d''3/2x/2| \
x3/2d''/2- [e''/2-d''/2]e''/2f''2e'' f''=g''/2-[g''/2f''/2-]| \
f''/2e''f''e''f''a''2x3/2|
x2 a''/2-[a''/2f''/2-]f''/2e''2x2x/2| \
xe'' f''a''2x3| \
x/2a''f''/2- [f''/2e''/2-]e''3/2 x3x/2e''/2-| \
e''/2f''a''2x3x/2a''|
f''e''2x3 x/2e''/2-[f''/2-e''/2]f''/2| \
a''2 x3x/2a''f''e''/2-| \
e''3/2x3x/2f'' ^g''/2-[b''/2-^g''/2]b''-| \
b''/2x3x/2 b''^g'' f''2|
x3/2f''2d''^c''d''2x/2| \
xd'' e''f''3/2-[f''/2e''/2-]e''/2f''=g''f''/2-| \
f''/2e''/2-[f''/2-e''/2]f''/2 e''f'' a''2 x2| \
x3/2a''f''e''2x2x/2|
x/2e''f''a''2x3x/2| \
a''f'' e''2 x3x/2a''/2-| \
a''-[a''/2-a''/2]a''/2 g''a'' x2 x/2e''f''/2-| \
f''/2e''d''e''x2x/2 a''2|
a''g''/2-[a''/2-g''/2] a''3/2a''b''=c'''3/2-[c'''/2b''/2-]b''/2| \
g''a''2e''3/2-[f''/2-e''/2]f''/2e''f''g''/2-| \
g''/2a''2x3/2 a''f'' e''d''| \
f''3/2-[g''/2-f''/2] g''/2f''d''^c''d''/2- [e''/2-d''/2]e''/2f''|
e''f'' g''a''3/2-[b''/2-a''/2]b''3/2=c'''d'''/2-| \
d'''/2c'''/2-[c'''/2b''/2-]b''/2 c'''b'' a''2 x2| \
x3/2e''f''e''/2- [e''/2d''/2-]d''/2e'' x2| \
x/2a''2a''g''a''3/2- [a''/2-a''/2]a''/2b''|
c'''2 b''g''/2-[a''/2-g''/2] a''3/2e''2f''/2-| \
f''/2e''/2-[f''/2-e''/2]f''/2 g''a''2x3/2a''f''/2-| \
f''/2e''d''f''3/2- [g''/2-f''/2]g''/2f'' d''^c''| \
d''/2-[e''/2-d''/2]e''/2f''e''f''g''a''3/2-[b''/2-a''/2]b''/2-|
b''=c''' d'''c''' b''/2-[b''/2a''/2-]a''/2^g''e''f''/2-| \
f''/2^g''=g''/2- [g''/2^d''/2-]^d''/2e'' g''^f'' =d''^d''/2-[^f''/2-^d''/2]| \
^f''/2=f''^c''=d''f''e''/2-[e''/2=c''/2-]c''/2 ^c''e''| \
^d''b' =c''/2-[^d''/2-c''/2]^d''/2=d''^a'b'd''^c''/2-|
[^c''/2=a'/2-]a'/2^a' =a''2 a''g''/2-[a''/2-g''/2] a''/2x3/2| \
x3/2e''f''/2-[f''/2e''/2-]e''/2 d''x3| \
x/2a''2a''g''a''3/2- [a''/2-a''/2]a''/2b''| \
=c'''3/2-[c'''/2b''/2-] b''/2g''a''2e''3/2-[f''/2-e''/2]f''/2|
e''f'' g''a''2x3/2a''f''/2-| \
f''/2e''d''/2- [f''/2-d''/2]f''3/2 g''f'' d''^c''/2-[d''/2-^c''/2]| \
d''/2e''f''e''f''g''a''3/2b''-| \
b''=c''' d'''/2-[d'''/2c'''/2-]c'''/2b''c'''b''a''3/2-|
a''/2x3x/2 e''f'' e''/2-[e''/2d''/2-]d''/2e''/2-| \
e''/2x2x/2a''2a'' g''a''-| \
a''a''/2-[b''/2-a''/2] b''/2c'''2b''g''a''3/2| \
e''2 f''e'' f''/2-[g''/2-f''/2]g''/2a''2x/2|
xa'' f''e'' d''f''3/2-[g''/2-f''/2]g''/2x/2| \
x/2d''^c''d''e''/2- [f''/2-e''/2]f''/2e'' f''g''| \
a''3/2-[b''/2-a''/2] b''3/2=c'''d'''c'''b''/2-[b''/2a''/2-]a''/2| \
^g''e'' f''^g'' =g''^d''/2-[e''/2-^d''/2] e''/2g''^f''/2-|
^f''/2=d''^d''^f''/2-[^f''/2=f''/2-]f''/2 ^c''=d'' e''f''| \
a''2 x3x/2a''/2- [a''/2f''/2-]f''/2e''-|e''
V:2
x3x/2
%%MIDI program 73
a/2 =c'/2e'/2x/2a/2 c'/2e'/2x| \
x8| \
x2 a/2c'/2e'/2x/2 a/2c'/2e'/2x2x/2| \
x8|
xd'/2f'/2<^g'/2d'/2f'/2^g'/2 x4| \
x6 x3/2f'/2| \
e'/2<d'/2f'/2e'/2 d'/2x4x3/2| \
x6 a/2c'/2e'/2x/2|
a/2[e'/2c'/2]x6x| \
x4 x/2a/2 (3c'e'ac'/2e'/2| \
x8| \
x3a/2 (3c'e'ac'/2 e'/2x3/2|
x8| \
x2 a/2[e'/2c'/2]x/2a/2 c'/2e'/2x3| \
x8| \
x/2d'/2f'/2^g'/2>d'/2f'/2^g'/2x4x/2|
x6 xf'/2e'/2| \
d'/2x/2f'/2e'/2 d'/2x4x3/2| \
x4 x3/2a/2 c'/2e'/2x/2a/2| \
c'/2e'/2x6x|
x4 a/2 (3c'e'ac'/2e'/2x/2| \
x8| \
x3a/2[e'/2c'/2] x/2a/2c'/2e'/2 x2| \
x8|
x3/2a/2 c'/2e'/2>a/2c'/2 e'/2x3x/2| \
x8| \
f/2 (3c'f'fc'/2f'/2x4x/2| \
x6 x/2f'/2e'/2d'/2|
x/2f'/2e'/2d'/2 x6| \
x4 xa/2 (3c'e'ac'/2| \
e'/2x6x3/2| \
x4 a/2c'/2<e'/2a/2 c'/2e'/2x|
x8| \
x2 x/2f/2c'/2f'/2>f/2c'/2f'/2x2x/2| \
x8| \
xf'/2e'/2 d'/2x/2f'/2e'/2 d'/2x3x/2|
x8| \
[c'/2a/2]e'/2x/2a/2 c'/2e'/2x4x| \
x6 x/2a/2c'/2<e'/2| \
a/2c'/2e'/2x6x/2|
x4 xa/2 (3c'e'a[e'/2c'/2]| \
x8| \
x3x/2a/2 c'/2e'/2x/2a/2 c'/2e'/2x| \
x8|
x2 f/2c'/2f'/2x/2 f/2c'/2f'/2x2x/2| \
x8| \
x[f'/2e'/2]d'/2 x/2f'/2e'/2d'/2 x4| \
x6 x3/2a/2|
c'/2<e'/2a/2c'/2 e'/2x4x3/2| \
x6 a/2c'/2e'/2x/2| \
a/2c'/2e'/2x6x/2| \
x4 x/2f/2 (3c'f'fc'/2f'/2|
x8| \
x3f'/2e'/2 d'/2x/2f'/2e'/2 d'/2x3/2| \
x8| \
x2 [c'/2a/2]e'/2x/2a/2 c'/2e'/2x3|
x8| \
x/2a/2c'/2<e'/2 a/2c'/2e'/2
V:3
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x6 x3/2
%%MIDI program 50
A,,,/2-| \
A,,,8-|
A,,,8-| \
A,,,8-| \
A,,,4- A,,,/2F,,,3-F,,,/2-| \
F,,,8-|
F,,,3D,,,4-D,,,-| \
D,,,2- D,,,/2D,,,4-D,,,3/2| \
x3/2A,,,6-A,,,/2-| \
A,,,8|
A,,,6- A,,,3/2F,,,/2-| \
F,,,6- F,,,/2A,,,3/2-| \
A,,,8-| \
A,,,4- A,,,A,,,3-|
A,,,4- A,,,/2B,,,3-B,,,/2| \
C,,3-C,,/2F,,,4-F,,,/2-| \
F,,,3F,,,4-F,,,-| \
F,,,2- F,,,/2D,,,4-D,,,3/2-|
D,,,3/2D,,,3-D,,,/2E,,,3-| \
E,,,/2A,,,6-A,,,x/2| \
A,,,6- A,,,A,,,-| \
A,,,6- A,,,/2B,,,3/2-|
B,,,2 C,,3-C,,/2x/2 F,,,2-| \
F,,,4- F,,,F,,,3-| \
F,,,4- F,,,/2D,,,3-D,,,/2-| \
D,,,3-D,,,/2D,,,3-D,,,/2x/2E,,,/2-|
E,,,3A,,,4-A,,,-| \
A,,,8-| \
A,,,8-| \
A,,,/2x/2F,,,6-F,,,|
A,,,8-| \
A,,,6- A,,,/2A,,,3/2-| \
A,,,4- A,,,3/2x/2 B,,,2-| \
B,,,3/2C,,3-C,,/2F,,,3-|
F,,,4- F,,,/2F,,,3-F,,,/2-| \
F,,,3-F,,,/2D,,,4-D,,,/2-| \
D,,,2- D,,,/2x/2D,,,3- D,,,/2E,,,3/2-| \
E,,,2 A,,,6-|
A,,,3/2A,,,6-A,,,/2-| \
A,,,/2x/2A,,,6-A,,,| \
B,,,3-B,,,/2C,,3-C,,/2x/2F,,,/2-| \
F,,,6- F,,,/2F,,,3/2-|
F,,,4- F,,,3/2x/2 D,,,2-| \
D,,,4- D,,,D,,,3-| \
D,,,/2E,,,3-E,,,/2 A,,,4-| \
A,,,3-A,,,/2A,,,4-A,,,/2-|
A,,,2- A,,,/2x/2A,,,4-A,,,-| \
A,,,2 F,,,6-|F,,,
V:4
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
%%MIDI program 4
c'3-c'/2b3-b/2x/2=g/2-| \
g6- g/2af/2-| \
f/2e3-e/2 ed c2-| \
c3/2B3-B/2c BA-|
Ae3/2-[e/2d/2-]d/2cB3-B/2| \
c6- c3/2f/2-| \
f-[f/2-f/2]f/2 ga3- a/2c'3/2-| \
c'2 x/2d'4-d'3/2-|
d'3/2f'3-f'/2e'3-| \
e'/2e'6-e'x/2| \
c'3-c'/2d'3-d'/2c'| \
ba2f3/2-[f/2e/2-]e/2de3/2-|
e2 c3-c/2x/2 f2-| \
f3/2f3/2-[g/2-f/2]g3/2a2a| \
b/2-[c'/2-b/2]c'3/2bc'd'3-d'/2-| \
d'2 f'/2e'/2d' e'3-e'/2b/2-|
b3c'4-c'-| \
c'2 x/2b4-b3/2-| \
b3/2g6-g/2-| \
g8|
af e3-e/2edc/2-| \
c3x4c/2-[c/2B/2-]| \
B/2A2e3/2- [e/2d/2-]d/2c B2-| \
B3/2c6-c/2-|
c/2x/2f3/2-[f/2-f/2]f/2ga3-a/2| \
c'3-c'/2d'4-d'/2-| \
d'3f'3- f'/2e'3/2-| \
e'2 e'6-|
e'3/2c'3-c'/2d'3-| \
d'/2c'ba2f3/2- [f/2e/2-]e/2d| \
e3-e/2c3-c/2f-| \
f2- f/2x/2f3/2-[g/2-f/2]g x/2a3/2|
x/2ab/2- [c'/2-b/2]c'3/2 bc' d'2-| \
d'3-d'/2f'/2 [e'/2d'/2-]d'/2e'3-| \
e'/2b3-b/2 x/2c'3-c'/2-| \
c'3-c'/2-[c'/2-c'/2] c'3a-|
a2- a/2x/2a4-a-|a/2
V:5
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
x8| \
x8| \
x8| \
x8|
%%MIDI program 4
ed c3-c/2cBA/2-| \
A3F3- F/2E3/2-| \
E/2E2c3/2- [c/2B/2-]B/2A G2-| \
G3/2E3-E/2A3-|
A/2x/2A3/2-[A/2-A/2]A/2Bc3-c/2| \
d3-d/2f4-f/2-| \
f3a3- a/2^g3/2-| \
^g2 a6-|
a3/2a3-a/2=g3-| \
g/2x/2a/2-[a/2g/2-] g/2e2c2B/2-[B/2A/2-]A/2| \
c3-c/2A3-A/2x/2a/2-| \
a3a2b3/2c'3/2-|
c'/2c'd'e'3/2 d'e' f'2-| \
f'3-f'/2a'/2 g'/2f'a2-a/2-| \
a^g3- ^g/2a3-a/2| \
^g3-^g/2x/2 a3-a/2f/2-|
f3e4-e-|e/2
V:6
%%MIDI channel 10
^dx3/2^d^dx2x/2^d| \
x2 ^d^d x2 x/2^dx/2| \
x^d ^dx2x/2^dx3/2| \
x/2^d^dx2x/2^d x3/2^d/2-|
^d/2^dx2x/2 ^dx2^d| \
^dx2x/2^dx3/2 ^d^d| \
x2 x/2^dx2^d^dx/2| \
x2 ^dx3/2^d^dx3/2|
x^d x2 ^d^d x2| \
x/2^dx3/2^d ^dx2x/2^d/2-| \
^d/2x2^d^dx2x/2^d| \
x3/2^d^dx2x/2 ^dx|
x^d ^dx2x/2^dx3/2| \
x/2^d/2-[^d/2-^d/2]^d/2 x3^d x3/2^d/2-| \
^d/2^dx2x/2 ^dx2^d/2-[^d/2-^d/2]| \
^d/2x3^dx3/2 ^d^d|
x2 x/2^dx2^d^dx/2| \
x2 ^dx3/2^d^dx3/2| \
x^d x2 ^d^d x2| \
x/2^dx3/2^d ^dx2x/2^d/2-|
^d/2x2^d^dx2x/2^d| \
x3/2^d^dx2x/2 C,,C,,| \
C,,C,, =D,,x/2D,,D,,C,,C,,C,,/2-| \
C,,/2C,,/2-[D,,/2-C,,/2]D,,/2 D,,D,, D,,C,, C,,/2-[C,,/2-C,,/2]C,,/2C,,/2-|
C,,/2D,,D,,D,,D,,/2- [D,,/2C,,/2-]C,,/2C,, C,,C,,| \
D,,D,,/2-[D,,/2-D,,/2] D,,/2D,,C,,C,,C,,C,,/2-[D,,/2-C,,/2]D,,/2| \
D,,D,, xC,, C,,/2-[C,,/2-C,,/2]C,,/2C,,D,,D,,/2-| \
D,,/2D,,D,,C,,/2-[C,,/2-C,,/2]C,,/2 C,,C,, D,,x/2D,,/2-|
D,,/2xC,,C,,C,,/2- [C,,/2-C,,/2]C,,/2D,, D,,D,,| \
D,,C,, C,,/2-[C,,/2-C,,/2]C,,/2C,,D,,D,,D,,x/2| \
C,,C,, C,,C,, D,,D,, D,,/2-[D,,/2-D,,/2]D,,/2C,,/2-| \
C,,/2C,,C,,C,,D,,/2- [D,,/2-D,,/2]D,,/2D,, D,,C,,|
C,,C,,/2-[C,,/2-C,,/2] C,,/2D,,D,,D,,D,,C,,/2-[C,,/2-C,,/2]C,,/2| \
C,,C,, D,,x D,,/2-D,,/2x/2C,,C,,C,,/2-| \
C,,/2C,,D,,/2- [D,,/2-D,,/2]D,,/2D,, D,,C,, C,,C,,/2-[C,,/2-C,,/2]| \
C,,/2D,,D,,D,,xC,,/2-[C,,/2-C,,/2]C,,/2 C,,C,,|
D,,D,, D,,D,,/2-[D,,/2C,,/2-] C,,/2C,,C,,C,,D,,/2-| \
D,,/2x/2x D,,C,, xC,, C,,/2-[D,,/2-C,,/2]D,,/2D,,/2-| \
D,,/2D,,D,,C,,C,,/2- [C,,/2-C,,/2]C,,/2C,, D,,x| \
D,,x/2C,,xC,,C,,D,,D,,/2-[D,,/2-D,,/2]D,,/2|
D,,C,, C,,C,, C,,/2-[D,,/2-C,,/2]D,,/2D,,D,,D,,/2-| \
D,,/2C,,C,,/2- [C,,/2-C,,/2]C,,/2C,, D,,D,, D,,D,,| \
C,,/2-[C,,/2-C,,/2]C,,/2C,,C,,D,,x/2D,, D,,C,,| \
xC,, C,,D,,/2-[D,,/2-D,,/2] D,,/2D,,D,,C,,C,,/2-|
C,,/2C,,/2-[C,,/2-C,,/2]C,,/2 D,,x D,,x C,,/2-[C,,/2-C,,/2]C,,/2C,,/2-| \
C,,/2C,,D,,D,,D,,/2- [D,,/2-D,,/2]D,,/2C,, C,,C,,| \
C,,D,,/2-D,,/2 x/2D,,xC,,C,,C,,/2-[C,,/2-C,,/2]C,,/2| \
D,,D,, D,,D,, C,,C,,/2-[C,,/2-C,,/2] C,,/2C,,D,,/2-|
D,,/2xD,,D,,/2-[D,,/2C,,/2-]C,,/2 C,,C,, C,,D,,| \
D,,/2-[D,,/2-D,,/2]D,,/2D,,C,,C,,C,,/2-[C,,/2-C,,/2]C,,/2 D,,D,,| \
D,,D,,/2-[D,,/2C,,/2-] C,,/2C,,C,,C,,D,,D,,D,,/2-| \
[D,,/2-D,,/2]D,,/2C,, C,,C,, C,,D,,/2-D,,/2 x/2D,,x/2|
x/2C,,C,,C,,C,,D,,/2-[D,,/2-D,,/2]D,,/2 D,,2-| \
[D,,/2-C,,/2-][D,,/2-C,,/2]D,,/2-[D,,-C,,][D,,-C,,][D,,D,,]xD,,/2- D,,/2x/2C,,| \
C,,C,, C,,D,,/2-[D,,/2-D,,/2] D,,/2D,,D,,C,,C,,/2-| \
C,,/2C,,/2-[C,,/2-C,,/2]C,,/2 D,,D,, D,,x C,,/2-[C,,/2-C,,/2]C,,/2C,,/2-|
C,,/2C,,D,,D,,D,,/2- [D,,/2-D,,/2]D,,/2^d x2| \
^d^d x2 x/2^dx3/2^d| \
^d

О, уже работает. Иду учиться работать с форматом.
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение LEGO-кирандиец » 16 май 2017, 19:20

А этот конвертер преобразовывает треки в midi-файл?
Лего и Кирандия - то, что делает мою жизнь интересной и весёлой.
Неплохо было бы, если эти два интереса слились. :!:
LEGO-кирандиец
(1) Пират с острова Котов
 
Сообщения: 68
Зарегистрирован: 20 авг 2016, 12:36
Откуда: Ангарск
Любимая часть Кирандии: Первая и третья
Любимые персонажи Кирандии: Малколм, Брэндон, Дарм
Почему Вы любите Легенду о Кирандии?: Потому что это игра детства, вызывающие приятные воспоминания и красивые образы. В основном, мои мысли совпадают с мыслями других "жителей" форума.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 28 май 2017, 22:20

Треки в миди файл надо сначала midjoinsc, затем writexmi.
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 28 май 2017, 22:35

Изображение
0.1.0 версия HD-ретекстура. Нажмите для раскрытия.
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение LEGO-кирандиец » 29 май 2017, 21:21

То есть треки нужно по порядку прогнать через программы?
А эта картинка - 3d ремейк стартовой локации? Получилось шикарно, только не увидел пилы. :wink:
Лего и Кирандия - то, что делает мою жизнь интересной и весёлой.
Неплохо было бы, если эти два интереса слились. :!:
LEGO-кирандиец
(1) Пират с острова Котов
 
Сообщения: 68
Зарегистрирован: 20 авг 2016, 12:36
Откуда: Ангарск
Любимая часть Кирандии: Первая и третья
Любимые персонажи Кирандии: Малколм, Брэндон, Дарм
Почему Вы любите Легенду о Кирандии?: Потому что это игра детства, вызывающие приятные воспоминания и красивые образы. В основном, мои мысли совпадают с мыслями других "жителей" форума.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 30 май 2017, 02:09

Если ты про озвучку, midi файлы к ней никакого отношения не имеют.
Объясняю, midijoinsc предназначен для склеивания кучи midi файлов в один.
Допустим, у тебя три файла - mysong.1.mid mysong.2.mid mysong.3.mid
Ты набираешь команду python3 midjoinsc.py "mysong.*.mid" joined.mid
и получаешь один файл joined.mid, содержащий все песни из этих трёх файлов.
Ты можешь прослушать каждую из них командой python3 midplay.py TimGM6mb.sf2 joined.mid номертреканачинаяснуля
Ты можешь сконвертировать файл в Miles XMI формат командой python3 writexmi.py joined.mid kyra1a.xmi
Если ты про озвучку, файлы распаковываются urvrm.py и упаковываются vrm-staging.py
Код: Выделить всё
import os,sys,struct,re
if len(sys.argv)!=3:
    sys.exit('usage: py unvrm.py [C:\path\]filename.vrm [C:\path]folder')
stream=open(sys.argv[1],'rb')
pak=stream.read()
stream.close()
header=pak[:struct.unpack('<I',pak[:4])[0]]
regexp=re.compile(b'(.{4})([^\x00]*)\x00',re.S)
files=regexp.findall(header)
if not os.path.exists(sys.argv[2]):
    os.makedirs(sys.argv[2])
for file in range(len(files)-1):
    if files[file][1].upper().endswith(b'.VOC'):
        wav=open(os.path.join(sys.argv[2],(files[file][1][:-3]+b'WAV').decode("utf-8")),"wb")
        voc=pak[struct.unpack('<I',files[file][0])[0]:struct.unpack('<I',files[file+1][0])[0]]
        start=struct.unpack('<H',voc[20:22])[0]
        freq=21739
        wave=b''
        while start<len(voc):
            if voc[start]==0:
                break
            elif voc[start]==1:
                size=struct.unpack('<I',voc[start+1:start+4]+b'\x00')[0]
                start+=4
                freq=int(1000000/(256-voc[start]))
                if voc[start+1]!=0:
                    sys.exit('unknown compression')
                wave+=voc[start+2:start+size]
                start+=size
            else:
                sys.exit('error')
        wav.write(b'RIFF')
        wav.write(struct.pack('<I',36+len(wave)))
        wav.write(b'WAVEfmt\x20\x10\x00\x00\x00\01\00\01\00')
        wav.write(struct.pack('<I',freq))
        wav.write(struct.pack('<I',freq))
        wav.write(struct.pack('<H',1))
        wav.write(struct.pack('<H',8))
        wav.write(b'data')
        wav.write(struct.pack('<I',len(wave)))
        wav.write(wave)
        wav.close()
    else:
        f=open(os.path.join(sys.argv[2],files[file][1].decode("utf-8")),"wb")
        f.write(pak[struct.unpack('<I',files[file][0])[0]:struct.unpack('<I',files[file+1][0])[0]])
        f.close()

Код: Выделить всё
import glob,sys,os,struct,io,audioop,numpy,resampy
if len(sys.argv)!=3:
  sys.exit('usage: py vrm.py [C:\path\]*.* [C:\path\]filename.vrm')
pak=dict()
for file in glob.glob(sys.argv[1]):
    f=open(file,'rb')
    pak[os.path.split(file)[-1].upper()]=f.read()
    f.close()
start=len(pak)*5+sum((len(filename) for filename in pak.keys()))+9
header=b''
body=b''
noise_called=False
def noise():
    global noise_called
    if noise_called:
       noise.called=False
       return 1
    else:
       noise.called=True
       return -1
for file in pak.keys():
    if file.endswith(".WAV"):
        stream=io.BytesIO(pak[file])
        if stream.read(4)!=b'RIFF':
            sys.exit('not valid wav format')
        expect=struct.unpack('<I',stream.read(4))[0]
        if stream.read(8)!=b'WAVEfmt\x20':
            sys.exit('failed')
        chunksize=struct.unpack('<I',stream.read(4))[0]
        chunk=stream.read(chunksize)
        fmt=struct.unpack('<H',chunk[0:2])[0]
        ch=struct.unpack('<H',chunk[2:4])[0]
        freq=struct.unpack('<I',chunk[4:8])[0]
        rate=struct.unpack('<I',chunk[8:12])[0]
        ba=struct.unpack('<H',chunk[12:14])[0]
        bps=struct.unpack('<H',chunk[14:16])[0]
        if fmt!=1:
            sys.exit('not a PCM')
        if ch!=1:
            sys.exit('not mono')
        if bps not in (8,16,24,32):
            sys.exit('bit not supported')
        if rate!=freq*ba or ba!=ch*bps/8:
            sys.exit('file corrupted')
        if stream.read(4)!=b'data':
            sys.exit('failed')
        datasize=struct.unpack('<I',stream.read(4))[0]
        expect-=20+chunksize
        expect=min(expect,datasize)
        if bps!=8:
            data=stream.read(divmod(expect,2)[0]*2)
            data=audioop.lin2lin(data,round(bps/8),1)
            data=audioop.bias(data, 1, 128)
            expect=len(data)
        else:
            data=stream.read(expect)
        if freq!=21739:
            data=numpy.array([num/255.0 for num in data]+[num/255.0 for num in data[-1:]*freq],dtype=numpy.float)
            data=resampy.resample(data,freq,21739)
            data=bytes((min(int(num),255) for num in (data*255.9).astype(int)))[:int(expect*21739/freq)]
        voc=b'Creative Voice File\x1a\x1a\x00\x0a\x01\x29\x11'
        chunkstart=0
        while True:
            chunk=data[chunkstart:chunkstart+16777215]
            if len(chunk)==0:
                break
            voc+=b'\x01'+struct.pack('<I',len(chunk)+2)[:3]+struct.pack('<B',round(256-(1000000/21739)))+b'\x00'
            voc+=chunk
            chunkstart+=16777215
        voc+=b'\x00'
        body+=voc
        header+=struct.pack('<I',start)+(file[:-3]+"VOC").encode('utf-8')+b'\x00'
        start+=len(voc)
    else:
        body+=pak[file]
        header+=struct.pack('<I',start)+file.encode('utf-8')+b'\x00'
        start+=len(pak[file])
header+=struct.pack('<I',start)+b'\x00\x00\x00\x00\x00'
stream=open(sys.argv[2],'wb')
stream.write(header)
stream.write(body)
stream.close()

То есть распаковка - python3 unvrm.py gemcut.vrm gemcut
и упаковка python3 vrm-staging.py "gemcut\*.*" gemcut.vrm
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение LEGO-кирандиец » 30 май 2017, 20:25

Я не про озвучку, а про личные музыкальные страсти, связанные с переводом муз. файла в midi.
Лего и Кирандия - то, что делает мою жизнь интересной и весёлой.
Неплохо было бы, если эти два интереса слились. :!:
LEGO-кирандиец
(1) Пират с острова Котов
 
Сообщения: 68
Зарегистрирован: 20 авг 2016, 12:36
Откуда: Ангарск
Любимая часть Кирандии: Первая и третья
Любимые персонажи Кирандии: Малколм, Брэндон, Дарм
Почему Вы любите Легенду о Кирандии?: Потому что это игра детства, вызывающие приятные воспоминания и красивые образы. В основном, мои мысли совпадают с мыслями других "жителей" форума.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 01 июн 2017, 20:49

Тогда тебе надо определить тонику и написать всё вручную, я полагаю? Нет простого способа сделать миди из готовой песни, но можно поставить scolily и напеть носом, получишь файл с приблизительными нотами.
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Re: Моя попытка стать разработчиком Кайрэндии

Сообщение bckpkol » 19 ноя 2017, 18:36

Нашёл баг в midplay.py. Не все mid файлы правильно проигрывались. Цитата с шикадая:
Any event values without the high-bit set (i.e. less than 0x80) are so-called "running status" values. If one of these bytes is encountered as a MIDI event, it is actually the first data byte of the event instead. The actual event is the same as the previous one. For example, these two lines are equivalent: (delay bytes omitted for clarity)

Так что
Код: Выделить всё
            msgtype=byte&240
            channel=byte&15

стало
Код: Выделить всё
            if byte&128:
                msgtype=byte&240
                channel=byte&15
            else:
                start-=1
Аватара пользователя
bckpkol
(3) Столичный горожанин
 
Сообщения: 224
Зарегистрирован: 02 янв 2011, 20:48
Откуда: город Бийск
Любимая часть Кирандии: Кирандия 3 и 4
Любимые персонажи Кирандии: Дарм, Зантия
Почему Вы любите Легенду о Кирандии?: За простоту, удобство интерфейса и лёгкую проходимость.

Пред.

Вернуться в Техничка

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron