prep.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. import os
  2. import sys
  3. import array
  4. import struct
  5. import zlib
  6. import textwrap
  7. import wave
  8. import audioop
  9. import xml.etree.ElementTree as ET
  10. import Image, ImageFont, ImageDraw, ImageChops
  11. import gameduino2 as gd2
  12. import gameduino2.convert
  13. import gameduino2.tmxreader
  14. from gameduino2.imbytes import imbytes
  15. def pad4(s):
  16. while len(s) % 4:
  17. s += chr(0)
  18. return s
  19. def stretch(im):
  20. d = imbytes(im)
  21. # print min(d), max(d)
  22. r = max(d) - min(d)
  23. return im.point(lambda x: (x - min(d)) * 255 / r)
  24. def getalpha(im):
  25. return im.split()[3]
  26. def tile(tw, th, im):
  27. tiles = []
  28. for y in range(0, im.size[1], th):
  29. for x in range(0, im.size[0], tw):
  30. tiles.append(im.crop((x, y, x + tw, y + th)))
  31. o = Image.new(im.mode, (tw, th * len(tiles)))
  32. for i,t in enumerate(tiles):
  33. o.paste(t, (0, i * th))
  34. return o
  35. def split(tw, th, im):
  36. tiles = []
  37. for y in range(0, im.size[1], th):
  38. for x in range(0, im.size[0], tw):
  39. tiles.append(im.crop((x, y, x + tw, y + th)))
  40. return tiles
  41. def join(tiles):
  42. (tw, th) = tiles[0].size
  43. o = Image.new(tiles[0].mode, (tw, th * len(tiles)))
  44. for i,t in enumerate(tiles):
  45. o.paste(t, (0, i * th))
  46. return o
  47. def setwidth(im, w):
  48. e = im.resize((w, im.size[1]))
  49. nch = len(e.mode)
  50. if nch == 1:
  51. black = 0
  52. else:
  53. black = (0,) * nch
  54. e.paste(black, (0, 0, e.size[0], e.size[1]))
  55. e.paste(im, (0, 0))
  56. return e
  57. def even(im):
  58. w = im.size[0]
  59. if (w % 2) == 0:
  60. return im
  61. else:
  62. return setwidth(im, w + 1)
  63. def extents(im):
  64. """ find pixel extents of im, as a box """
  65. w,h = im.size
  66. cols = [set(imbytes(im.crop((i, 0, i + 1, im.size[1])))) != set([0]) for i in range(w)]
  67. rows = [set(imbytes(im.crop((0, i, im.size[0], i + 1)))) != set([0]) for i in range(h)]
  68. if not True in cols:
  69. return (0, 0, 0, 0)
  70. else:
  71. x0 = cols.index(True)
  72. y0 = rows.index(True)
  73. while not cols[-1]:
  74. cols.pop()
  75. while not rows[-1]:
  76. rows.pop()
  77. return (x0, y0, len(cols), len(rows))
  78. def ul(x):
  79. return str(x) + "UL"
  80. def cname(s):
  81. """ make name s C-friendly """
  82. for c in "-+.":
  83. s = s.replace(c, "_")
  84. return s.upper()
  85. def preview(np, fmt, size, data):
  86. def chan(x):
  87. return Image.fromstring("L", size, (255 * x).astype(np.uint8))
  88. if fmt == gd2.L1:
  89. r = Image.fromstring("1", size, data)
  90. elif fmt == gd2.L8:
  91. r = Image.fromstring("L", size, data)
  92. else:
  93. d8 = np.array(data)
  94. a = np.ones(size[0] * size[1])
  95. (r, g, b) = (a, a, a)
  96. if fmt == gd2.ARGB4:
  97. d16 = np.array(array.array('H', data.tostring()))
  98. a = (15 & (d16 >> 12)) / 15.
  99. r = (15 & (d16 >> 8)) / 15.
  100. g = (15 & (d16 >> 4)) / 15.
  101. b = (15 & (d16 >> 0)) / 15.
  102. elif fmt == gd2.RGB565:
  103. d16 = np.array(array.array('H', data.tostring()))
  104. r = (31 & (d16 >> 11)) / 31.
  105. g = (63 & (d16 >> 5)) / 63.
  106. b = (31 & (d16 >> 0)) / 31.
  107. elif fmt == gd2.ARGB1555:
  108. d16 = np.array(array.array('H', data.tostring()))
  109. a = (1 & (d16 >> 15))
  110. r = (31 & (d16 >> 10)) / 31.
  111. g = (31 & (d16 >> 5)) / 31.
  112. b = (31 & (d16 >> 0)) / 31.
  113. elif fmt == gd2.ARGB2:
  114. a = (3 & (d8 >> 6)) / 3.
  115. r = (3 & (d8 >> 4)) / 3.
  116. g = (3 & (d8 >> 2)) / 3.
  117. b = (3 & (d8 >> 0)) / 3.
  118. elif fmt == gd2.RGB332:
  119. r = (7 & (d8 >> 5)) / 7.
  120. g = (7 & (d8 >> 2)) / 7.
  121. b = (3 & (d8 >> 0)) / 3.
  122. elif fmt == gd2.L4:
  123. hi = d8 >> 4
  124. lo = d8 & 15
  125. d4 = np.column_stack((hi, lo)).flatten()
  126. r = d4 / 15.
  127. g = r
  128. b = r
  129. o = Image.merge("RGB", [chan(c) for c in (r, g, b)])
  130. bg = Image.new("RGB", size, (128, 128, 128))
  131. r = Image.composite(o, bg, chan(a))
  132. r = r.resize((r.size[0] * 5, r.size[1] * 5), Image.NEAREST)
  133. return r
  134. import gameduino2.base
  135. def pma(im):
  136. im = im.convert("RGBA")
  137. (r,g,b,a) = im.split()
  138. (r,g,b) = [ImageChops.multiply(a, c) for c in (r,g,b)]
  139. return Image.merge("RGBA", (r, g, b, a))
  140. class EVE(gameduino2.base.GD2):
  141. def __init__(self):
  142. self.d = ""
  143. def c(self, s):
  144. self.d += s
  145. def chunker(seq, size):
  146. return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
  147. class AssetBin(gameduino2.base.GD2):
  148. asset_file = None
  149. prefix = ""
  150. previews = False
  151. def __init__(self):
  152. self.alldata = ""
  153. self.commands = ""
  154. self.defines = []
  155. self.inits = []
  156. self.handle = 0
  157. self.np = None
  158. self.bitmaps = []
  159. # Set defaults for FT800. target_810() modifies these
  160. self.device = 'GD2'
  161. self.maxram = 256 * 1024
  162. self.maxhandles = 15
  163. def target_810(self):
  164. self.device = 'GD3'
  165. self.maxram = 1024 * 1024
  166. self.maxhandles = 32
  167. def define(self, n, v):
  168. self.defines.append((self.prefix + n, v))
  169. def add(self, name, s):
  170. if name:
  171. self.defines.append((name, ul(len(self.alldata))))
  172. self.alldata += s
  173. def c(self, s):
  174. self.commands += s
  175. def addim(self, name, im, fmt, dither = False):
  176. (_, imgdata) = gameduino2.convert.convert(im, dither, fmt = fmt)
  177. self.add(name, imgdata.tostring())
  178. (w, h) = im.size
  179. if name:
  180. self.defines.append(("%s_WIDTH" % name, w))
  181. self.defines.append(("%s_HEIGHT" % name, h))
  182. def align(self, n):
  183. while (len(self.alldata) % n) != 0:
  184. self.alldata += chr(0)
  185. def load_bitmap(self, im, fmt, dither = False):
  186. (w, h) = im.size
  187. self.align(2)
  188. # aw is aligned width
  189. # For L1, L2, L4 formats the width must be a whole number of bytes
  190. if fmt == gd2.L1:
  191. aw = (w + 7) & ~7
  192. elif fmt == gd2.L2:
  193. aw = (w + 3) & ~3
  194. elif fmt == gd2.L4:
  195. aw = (w + 1) & ~1
  196. else:
  197. aw = w
  198. if aw != w:
  199. im = setwidth(im, aw)
  200. if hasattr(im, "imgdata"):
  201. imgdata = im.imgdata
  202. else:
  203. (_, imgdata) = gameduino2.convert.convert(im, dither, fmt = fmt)
  204. r = len(self.alldata)
  205. self.alldata += imgdata.tostring()
  206. return r
  207. def load_handle(self, name, images, fmt,
  208. dither = False,
  209. filter = gd2.NEAREST,
  210. scale = 1,
  211. rotating = False):
  212. if self.maxhandles <= self.handle:
  213. print "Error: too many bitmap handles used, limit is %d" % self.maxhandles
  214. sys.exit(1)
  215. (w, h) = images[0].size
  216. self.align(2)
  217. if name is not None:
  218. self.define("%s_HANDLE" % name, self.handle)
  219. name = self.prefix + name
  220. self.defines.append(("%s_WIDTH" % name, w))
  221. self.defines.append(("%s_HEIGHT" % name, h))
  222. self.defines.append(("%s_CELLS" % name, len(images)))
  223. self.bitmaps.append((name.lower(), w, h, w / 2, h / 2, len(self.alldata), fmt, self.handle))
  224. self.BitmapHandle(self.handle)
  225. self.BitmapSource(len(self.alldata))
  226. if not rotating:
  227. (vw, vh) = (scale * w, scale * h)
  228. vsz = 0
  229. else:
  230. vsz = int(scale * max(w, h))
  231. self.define("%s_SIZE" % name, vsz)
  232. (vw, vh) = (vsz, vsz)
  233. self.BitmapSize(filter, gd2.BORDER, gd2.BORDER, vw, vh)
  234. if self.device == 'GD3':
  235. self.BitmapSizeH(vw >> 9, vh >> 9)
  236. if name is not None:
  237. self.inits.append("static const shape_t %s_SHAPE = {%d, %d, %d, %d};" % (name, self.handle, w, h, vsz))
  238. # aw is aligned width
  239. # For L1, L2, L4 formats the width must be a whole number of bytes
  240. if fmt == gd2.L1:
  241. aw = (w + 7) & ~7
  242. elif fmt == gd2.L2:
  243. aw = (w + 3) & ~3
  244. elif fmt == gd2.L4:
  245. aw = (w + 1) & ~1
  246. else:
  247. aw = w
  248. bpl = {
  249. gd2.ARGB1555 : 2 * aw,
  250. gd2.L1 : aw / 8,
  251. gd2.L2 : aw / 4,
  252. gd2.L4 : aw / 2,
  253. gd2.L8 : aw,
  254. gd2.RGB332 : aw,
  255. gd2.ARGB2 : aw,
  256. gd2.ARGB4 : 2 * aw,
  257. gd2.RGB565 : 2 * aw,
  258. gd2.PALETTED : aw}[fmt]
  259. self.BitmapLayout(fmt, bpl, h)
  260. if self.device == 'GD3':
  261. self.BitmapLayoutH(bpl >> 10, h >> 9)
  262. for i,im in enumerate(images):
  263. if aw != w:
  264. im = setwidth(im, aw)
  265. if hasattr(im, "imgdata"):
  266. imgdata = im.imgdata
  267. else:
  268. (_, imgdata) = gameduino2.convert.convert(im, dither, fmt = fmt)
  269. """
  270. if self.previews:
  271. if not self.np:
  272. import numpy
  273. self.np = numpy
  274. preview(self.np, fmt, im.size, imgdata).save("previews/%s-%s-%02d.png" % (self.name, name, i))
  275. """
  276. self.alldata += imgdata.tostring()
  277. self.handle += 1
  278. def load_font(self, name, ims, widths, fmt, **args):
  279. trim0 = 0
  280. while ims[trim0] is None:
  281. trim0 += 1
  282. p0 = len(self.alldata)
  283. h = self.handle
  284. tims = ims[trim0:]
  285. self.load_handle(name, tims, fmt, **args)
  286. self.align(4)
  287. p1 = len(self.alldata)
  288. # Compute memory required by one char
  289. onechar = (p1 - p0) / len(tims)
  290. # print name, 'font requires', (p1 - p0), 'bytes'
  291. sz = ims[trim0].size
  292. self.BitmapSource(p0 - (onechar * trim0))
  293. widths = [max(0, w) for w in widths]
  294. dblock = array.array('B', widths).tostring() + struct.pack("<5i", fmt, 1, sz[0], sz[1], p0 - (onechar * trim0))
  295. self.alldata += dblock
  296. self.cmd_setfont(h, p1)
  297. def load_ttf(self, name, ttfname, size, format, botchar = 32, topchar = 127, charset = None):
  298. font = ImageFont.truetype(ttfname, size)
  299. if charset is not None:
  300. topchar = botchar + len(charset)
  301. rr = [ord(c) for c in charset]
  302. else:
  303. rr = range(botchar, topchar + 1)
  304. sizes = {c:font.getsize(chr(c)) for c in rr}
  305. fw = max([w for (w, _) in sizes.values()])
  306. fh = max([h for (_, h) in sizes.values()])
  307. # Determine true pixel extents of all characters
  308. im = Image.new("L", (fw+16, fh+16))
  309. dr = ImageDraw.Draw(im)
  310. for i in rr:
  311. dr.text((8,8), chr(i), font=font, fill=255)
  312. alle = gd2.prep.extents(im)
  313. # render and crop the characters to the extents
  314. ims = [None] * botchar
  315. for i in rr:
  316. im = Image.new("L", (fw+16, fh+16))
  317. dr = ImageDraw.Draw(im)
  318. dr.text((8, 8), chr(i), font=font, fill=255)
  319. ims.append(im.crop(alle))
  320. widths = [sizes.get(c, (0,0))[0] for c in range(128)]
  321. if charset is not None:
  322. for i,c in enumerate(charset):
  323. widths[botchar + i] = sizes.get(ord(c), (0,0))[0]
  324. self.load_font(name, ims, widths, format)
  325. def load_tiles(self, name, file_name, scale = None, preview = False):
  326. world_map = gameduino2.tmxreader.TileMapParser().parse_decode(file_name)
  327. # print("loaded map:", world_map.map_file_name)
  328. x_pixels = world_map.pixel_width
  329. y_pixels = world_map.pixel_height
  330. # print("map size in pixels:", x_pixels, y_pixels)
  331. # print("tile size used:", world_map.tilewidth, world_map.tileheight)
  332. # print("tiles used:", world_map.width, world_map.height)
  333. # print("found '", len(world_map.layers), "' layers on this map")
  334. layers = [l for l in world_map.layers if hasattr(l, 'decoded_content')]
  335. w,h = (world_map.width, world_map.height)
  336. ts = world_map.tile_sets[0]
  337. tw = int(ts.tilewidth)
  338. th = int(ts.tileheight)
  339. if scale is not None:
  340. stw,sth = (int(tw * scale), int(th * scale))
  341. else:
  342. stw,sth = tw,th
  343. used = set()
  344. for layer in layers:
  345. used |= set(layer.decoded_content)
  346. # used = sorted(used)
  347. used = sorted(used - set([0]))
  348. def reindex(i):
  349. if i == 0:
  350. return None
  351. else:
  352. return used.index(i)
  353. def fetchtile(l, i, j):
  354. if (i < world_map.width) and (j < world_map.height):
  355. return reindex(l.decoded_content[i + (j * world_map.width)])
  356. else:
  357. return None
  358. eve = EVE()
  359. # print world_map.width * world_map.height * 4
  360. for j in range(0, world_map.height, 4):
  361. for i in range(0, world_map.width, 4):
  362. for layer in layers:
  363. for y in range(4):
  364. for x in range(4):
  365. t = fetchtile(layer, i + x, j + y)
  366. # if i < (480 / 16) and j < (272 / 16): print 16 * (i + x), 16 * (j + y), "tile", t
  367. if t is not None:
  368. eve.Vertex2ii(stw * x, sth * y, t / 128, t % 128)
  369. else:
  370. eve.Nop()
  371. # assert 0
  372. stride = ((w + 3) / 4)
  373. self.add(name, struct.pack("6H", w * stw, h * sth, stw * 4, sth * 4, stride, len(layers)) + eve.d)
  374. self.tile_files = world_map.tile_sets[0].images[0].source
  375. # print 'Size of tiles: %d (compressed %d)' % (len(eve.d), len(zlib.compress(eve.d)))
  376. # print 'Tile size', (tw, th)
  377. im = pma(Image.open(self.tile_files))
  378. def extract(i):
  379. if hasattr(ts, 'columns'):
  380. w = int(ts.columns)
  381. elif not hasattr(ts, 'spacing'):
  382. w = im.size[0] / tw
  383. else:
  384. w = (im.size[0] + ts.spacing) / (tw + ts.spacing)
  385. x = ts.margin + (tw + ts.spacing) * (i % w)
  386. y = ts.margin + (th + ts.spacing) * (i / w)
  387. r = im.crop((x + 0, y + 0, x + tw, y + th))
  388. if scale:
  389. r = r.resize((stw, sth), Image.ANTIALIAS)
  390. return r
  391. for i,g128 in enumerate(chunker(used, 128)):
  392. self.load_handle(None, [extract(t - 1) for t in g128], gd2.ARGB4, dither=0)
  393. if preview:
  394. pv = Image.new("RGB", (tw * w, th * h))
  395. layer = layers[0]
  396. for y in range(pv.size[1] / th):
  397. for x in range(pv.size[0] / tw):
  398. t = fetchtile(layer, x, y)
  399. pv.paste(extract(used[t] - 1), (16 * x, 16 * y))
  400. return pv
  401. def load_atlas(self, file_name, scale = None):
  402. tree = ET.parse(file_name)
  403. root = tree.getroot()
  404. if root.tag == 'TextureAtlas':
  405. imagepath = root.attrib['imagePath']
  406. im = Image.open(imagepath)
  407. fmt = gd2.ARGB4
  408. for child in root.iter('SubTexture'):
  409. a = child.attrib
  410. # print a
  411. (x, y, width, height) = [int(a[n]) for n in ["x", "y", "width", "height"]]
  412. sub = im.crop((x, y, x + width, y + height))
  413. if scale is not None:
  414. width = int(width * scale)
  415. height = int(height * scale)
  416. sub = sub.resize((width, height), Image.ANTIALIAS)
  417. src = self.load_bitmap(sub, fmt)
  418. nm = a['name']
  419. nm = nm.replace('.png', '')
  420. lp = len(self.alldata)
  421. self.inits.append("#define atlas_%s __fromatlas(%s)" % (nm, ul(lp)))
  422. self.alldata += struct.pack("HHHHIB", width, height, width / 2, height / 2, src, fmt)
  423. sub.save("%s.png" % nm)
  424. """
  425. def dxt1(self, imagefile):
  426. import numpy
  427. im = Image.open(imagefile).resize((480,272), Image.ANTIALIAS)
  428. dxt = "%s.dxt" % imagefile
  429. if not os.access(dxt, os.R_OK):
  430. im.save("tmp.png")
  431. assert os.system("squishpng tmp.png %s" % dxt) == 0
  432. sz = (480 / 4, 272 / 4)
  433. def rgb(cs):
  434. r = (cs >> 11) << 3
  435. g = 0xff & ((cs >> 5) << 2)
  436. b = 0xff & (cs << 3)
  437. return (r,g,b)
  438. def rgbim(cs):
  439. return Image.merge("RGB", [Image.fromarray(c.astype(numpy.uint8).reshape(*sz)) for c in rgb(cs)])
  440. def morton1(x):
  441. v = x & 0x55555555
  442. v = (v | (v >> 1)) & 0x33333333
  443. v = (v | (v >> 2)) & 0x0F0F0F0F
  444. v = (v | (v >> 4)) & 0x00FF00FF
  445. v = (v | (v >> 8)) & 0x0000FFFF
  446. return v.astype(numpy.uint16)
  447. h = open(dxt)
  448. h.read(8)
  449. c0s = []
  450. c1s = []
  451. bits = []
  452. for i in range(sz[0] * sz[1]):
  453. tile = h.read(8)
  454. c0,c1,bit = struct.unpack("2HI", tile)
  455. b0 = bit & 0x55555555
  456. b1 = (bit >> 1) & 0x55555555
  457. is0 = ~b1 & ~b0
  458. is1 = ~b1 & b0
  459. is2 = b1 & ~b0
  460. is3 = b1 & b0
  461. if c0<=c1:
  462. if bit == 0xaaaaaaaa:
  463. # print c0<c1,hex(bit)
  464. r0,g0,b0 = rgb(c0)
  465. r1,g1,b1 = rgb(c1)
  466. r = (r0 + r1) / 2
  467. g = (g0 + g1) / 2
  468. b = (b0 + b1) / 2
  469. # r,g,b = (255,0,255)
  470. c0 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
  471. c1 = c0
  472. bit = 0
  473. else:
  474. if 0:
  475. for i in range(0, 32, 2):
  476. fld = (3 & (bit >> i))
  477. if 3 == fld:
  478. print c0, c1, hex(bit), i
  479. assert 0
  480. # Map as follows:
  481. # 0 -> 0
  482. # 1 -> 3
  483. # 2 -> 1
  484. bit = (is1 * 3) + (is2 * 1)
  485. assert is3 == 0
  486. else:
  487. # 0 -> 0
  488. # 1 -> 3
  489. # 2 -> 1
  490. # 3 -> 2
  491. bit = (is1 * 3) + (is2 * 1) + (is3 * 2)
  492. if 0:
  493. c0 = 63 << 5 # green
  494. c1 = 31 << 11 # red
  495. bit = 0x0f0f0f0f
  496. c0s.append(c0)
  497. c1s.append(c1)
  498. bits.append(bit)
  499. c0s = numpy.array(c0s, numpy.uint16)
  500. c1s = numpy.array(c1s, numpy.uint16)
  501. bits = numpy.array(bits, numpy.uint32)
  502. bits0 = morton1(bits)
  503. bits1 = morton1(bits >> 1)
  504. if 1:
  505. def im44(v):
  506. im = Image.new("1", (4,4))
  507. pix = im.load()
  508. for i in range(4):
  509. for j in range(4):
  510. if v & (1 << (4 * i + j)):
  511. pix[j,i] = 255
  512. return im
  513. ims = [im44(i) for i in range(65536)]
  514. b0im = Image.new("1", (480, 272))
  515. b1im = Image.new("1", (480, 272))
  516. xys = [(x,y) for y in range(0, 272, 4) for x in range(0, 480, 4)]
  517. for i,(x,y) in enumerate(xys):
  518. b0im.paste(ims[bits0[i]], (x, y))
  519. b1im.paste(ims[bits1[i]], (x, y))
  520. class MockImage:
  521. def __init__(self, s, size):
  522. self.imgdata = s
  523. self.size = size
  524. self.load_handle("BACKGROUND_COLOR", [MockImage(c0s, sz), MockImage(c1s, sz)], gd2.RGB565, scale = 4)
  525. self.load_handle("BACKGROUND_BITS", [b0im, b1im], gd2.L1)
  526. """
  527. def load_sample(self, name, filename):
  528. f = wave.open(filename, "rb")
  529. if f.getnchannels() != 1:
  530. print "Sorry - .wav file must be mono"
  531. sys.exit(1)
  532. if f.getsampwidth() != 2:
  533. print "Sorry - .wav file must be 16-bit"
  534. sys.exit(1)
  535. freq = f.getframerate()
  536. pcm16 = f.readframes(f.getnframes())
  537. (adpcm, _) = audioop.lin2adpcm(pcm16, f.getsampwidth(), (0,0))
  538. adpcm = adpcm[:len(adpcm) & ~7]
  539. da = array.array('B', [((ord(c) >> 4) | ((15 & ord(c)) << 4)) for c in adpcm])
  540. self.align(8)
  541. self.add(name, da.tostring())
  542. self.define(name + "_LENGTH", len(da))
  543. self.define(name + "_FREQ", freq)
  544. header = None
  545. header_intro = ""
  546. def make(self):
  547. if self.header is None:
  548. name = self.__class__.__name__.lower() + "_assets.h"
  549. else:
  550. name = self.header
  551. self.name = name
  552. self.addall()
  553. if len(self.alldata) > self.maxram:
  554. print "Error: The data (%d bytes) is larger the the %s RAM (%d)" % (len(self.alldata), self.device, self.maxram)
  555. sys.exit(1)
  556. self.defines.append((self.prefix + "ASSETS_END", ul(len(self.alldata))))
  557. self.cmd_inflate(0)
  558. calldata = zlib.compress(self.alldata, 9)
  559. commandblock = self.commands + pad4(calldata)
  560. print 'Assets report'
  561. print '-------------'
  562. print 'Header file: %s' % self.header
  563. print '%s RAM used: %d' % (self.device, len(self.alldata))
  564. if not self.asset_file:
  565. print 'Flash used: %d' % len(commandblock)
  566. else:
  567. print 'Output file: %s' % self.asset_file
  568. print 'File size: %d' % len(calldata)
  569. hh = open(name, "w")
  570. hh.write(self.header_intro)
  571. for (nm,v) in self.defines:
  572. print >>hh, "#define %s %s" % (nm, v)
  573. p = self.prefix
  574. if self.asset_file is None:
  575. print >>hh, "static const PROGMEM uint8_t %s__assets[%d] = {" % (p, len(commandblock))
  576. print >>hh, textwrap.fill(", ".join(["%d" % ord(c) for c in commandblock]))
  577. print >>hh, "};"
  578. print >>hh, "#define %sLOAD_ASSETS() (GD.copy(%s__assets, sizeof(%s__assets)), GD.loadptr = %sASSETS_END)" % (p, p, p, p)
  579. else:
  580. open(self.asset_file, "wb").write(commandblock)
  581. print >>hh, '#define %sLOAD_ASSETS() (GD.safeload("%s"), GD.loadptr = %sASSETS_END)' % (p, self.asset_file, p)
  582. print >>hh
  583. for i in self.inits:
  584. print >>hh, i
  585. self.dump_bitmaps(hh)
  586. self.extras(hh)
  587. def dump_bitmaps(self, hh):
  588. hh.write("struct {\n")
  589. hh.write("".join([" Bitmap %s;\n" % bm[0] for bm in self.bitmaps]))
  590. hh.write("} bitmaps = {\n")
  591. fmt = " /* %16s */ {{%3d, %3d}, {%3d, %3d}, %#8xUL, %2d, %2d}"
  592. hh.write(",\n".join([fmt % bm for bm in self.bitmaps]))
  593. hh.write("\n};\n")
  594. def addall(self):
  595. pass
  596. def extras(self, hh):
  597. pass
  598. class ForthAssetBin(AssetBin):
  599. def make(self):
  600. if self.header is None:
  601. name = self.__class__.__name__.lower() + "_assets.fs"
  602. else:
  603. name = self.header
  604. self.name = name
  605. self.addall()
  606. if len(self.alldata) > self.maxram:
  607. print "Error: The data (%d bytes) is larger the the GD2 RAM" % len(self.alldata)
  608. sys.exit(1)
  609. self.defines.append((self.prefix + "ASSETS_END", ul(len(self.alldata))))
  610. self.cmd_inflate(0)
  611. calldata = zlib.compress(self.alldata)
  612. print 'Assets report'
  613. print '-------------'
  614. print 'Header file: %s' % self.header
  615. print 'GD2 RAM used: %d' % len(self.alldata)
  616. if not self.asset_file:
  617. print 'Flash used: %d' % len(calldata)
  618. else:
  619. print 'Output file: %s' % self.asset_file
  620. print 'File size: %d' % len(calldata)
  621. commandblock = self.commands + calldata
  622. commandblock += chr(0) * ((-len(commandblock)) & 3)
  623. commandblock32 = array.array('I', commandblock)
  624. hh = open(name, "w")
  625. print >>hh, "base @"
  626. print >>hh, "hex"
  627. if self.asset_file is None:
  628. print >>hh, textwrap.fill(" ".join(["%08x GD.c" % c for c in commandblock32]))
  629. else:
  630. open(self.asset_file, "wb").write(commandblock)
  631. print >>hh, "decimal"
  632. for (nm,v) in self.defines:
  633. print >>hh, "%-8s constant %s" % (str(v).replace('UL', ''), nm)
  634. print >>hh, "base !"
  635. self.extras(hh)