First of all, my question is different to How do I convert image to 2-bit per pixel? and unfortunately its solution does not work in my case...
I need to convert images to 2-bit per pixel grayscale BMP format. The sample image has the following properties:
Color Model: RGB
Depth: 4
Is Indexed: 1
Dimension: 800x600
Size: 240,070 bytes (4 bits per pixel but only last 2 bits are used to identify the gray scales as 0/1/2/3 in decimal or 0000/0001/0010/0011 in binary, plus 70 bytes BMP metadata or whatever)
The Hex values of the beginning part of the sample BMP image:
The 3s represent white pixels at the beginning of the image. Further down there are some 0s, 1s and 2s representing black, dark gray and light gray:
With the command below,
convert pic.png -colorspace gray +matte -depth 2 out.bmp
I can get visually correct 4-level grayscale image, but wrong depth or size per pixel:
Color Model: RGB
Depth: 8 (expect 4)
Dimension: 800x504
Size: 1,209,738 bytes (something like 3 bytes per pixel, plus metadata)
(no mention of indexed colour space)
Please help...
OK, I have written a Python script following Mark's hints (see comments under original question) to manually create a 4-level gray scale BMP with 4bpp. This specific BMP format construction is for the 4.3 inch e-paper display module made by WaveShare. Specs can be found here: http://www.waveshare.com/wiki/4.3inch_e-Paper
Here's how to pipe the original image to my code and save the outcome.
convert in.png -colorspace gray +matte -colors 4 -depth 2 -resize '800x600>' pgm:- | ./4_level_gray_4bpp_BMP_converter.py > out.bmp
Contents of 4_level_gray_4bpp_BMP_converter.py:
#!/usr/bin/env python
"""
### Sample BMP header structure, total = 70 bytes
### !!! little-endian !!!
Bitmap file header 14 bytes
42 4D "BM"
C6 A9 03 00 FileSize = 240,070 <= dynamic value
00 00 Reserved
00 00 Reserved
46 00 00 00 Offset = 70 = 14+56
DIB header (bitmap information header)
BITMAPV3INFOHEADER 56 bytes
28 00 00 00 Size = 40
20 03 00 00 Width = 800 <= dynamic value
58 02 00 00 Height = 600 <= dynamic value
01 00 Planes = 1
04 00 BitCount = 4
00 00 00 00 compression
00 00 00 00 SizeImage
00 00 00 00 XPerlPerMeter
00 00 00 00 YPerlPerMeter
04 00 00 00 Colours used = 4
00 00 00 00 ColorImportant
00 00 00 00 Colour definition index 0
55 55 55 00 Colour definition index 1
AA AA AA 00 Colour definition index 2
FF FF FF 00 Colour definition index 3
"""
# to insert File Size, Width and Height with hex strings in order
BMP_HEADER = "42 4D %s 00 00 00 00 46 00 00 00 28 00 00 00 %s %s 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 55 55 55 00 AA AA AA 00 FF FF FF 00"
BMP_HEADER_SIZE = 70
BPP = 4
BYTE = 8
ALIGNMENT = 4 # bytes per row
import sys
from re import findall
DIMENTIONS = 1
PIXELS = 3
BLACK = "0"
DARK_GRAY = "1"
GRAY = "2"
WHITE = "3"
# sample data:
# ['P5\n', '610 590\n', '255\n', '<1 byte per pixel for 4 levels of gray>']
# where item 1 is always P5, item 2 is width heigh, item 3 is always 255, items 4 is pixels/colours
data = sys.stdin.readlines()
width = int(data[DIMENTIONS].strip().split(' ')[0])
height = int(data[DIMENTIONS].strip().split(' ')[1])
if not width*height == len(data[PIXELS]):
print "Error: pixel data (%s bytes) and image size (%dx%d pixels) do not match" % (len(data[PIXELS]),width,height)
sys.exit()
colours = [] # enumerate 4 gray levels
for p in data[PIXELS]:
if not p in colours:
colours.append(p)
if len(colours) == 4:
break
# it's possible for the converted pixels to have less than 4 gray levels
colours = sorted(colours) # sort from low to high
# map each colour to e-paper gray indexes
# creates hex string of pixels
# e.g. "0033322222110200....", which is 4 level gray with 4bpp
if len(colours) == 1: # unlikely, but let's have this case here
pixels = data[PIXELS].replace(colours[0],BLACK)
elif len(colours) == 2: # black & white
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],WHITE)
elif len(colours) == 3:
pixels = data[PIXELS].replace(colours[0],DARK_GRAY)\
.replace(colours[1],GRAY)\
.replace(colours[2],WHITE)
else: # 4 grays as expected
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],DARK_GRAY)\
.replace(colours[2],GRAY)\
.replace(colours[3],WHITE)
# BMP pixel array starts from last row to first row
# and must be aligned to 4 bytes or 8 pixels
padding = "F" * ((BYTE/BPP) * ALIGNMENT - width % ((BYTE/BPP) * ALIGNMENT))
aligned_pixels = ''.join([pixels[i:i+width]+padding for i in range(0, len(pixels), width)][::-1])
# convert hex string to represented byte values
def Hex2Bytes(hexStr):
hexStr = ''.join(hexStr.split(" "))
bytes = []
for i in range(0, len(hexStr), 2):
byte = int(hexStr[i:i+2],16)
bytes.append(chr(byte))
return ''.join(bytes)
# convert integer to 4-byte little endian hex string
# e.g. 800 => 0x320 => 00000320 (big-endian) =>20030000 (little-endian)
def i2LeHexStr(i):
be_hex = ('0000000'+hex(i)[2:])[-8:]
n = 2 # split every 2 letters
return ''.join([be_hex[i:i+n] for i in range(0, len(be_hex), n)][::-1])
BMP_HEADER = BMP_HEADER % (i2LeHexStr(len(aligned_pixels)/(BYTE/BPP)+BMP_HEADER_SIZE),i2LeHexStr(width),i2LeHexStr(height))
sys.stdout.write(Hex2Bytes(BMP_HEADER+aligned_pixels))
Edit: everything about this e-paper display and my code to display things on it can be found here: https://github.com/yy502/ePaperDisplay
This works for me in Imagemagick 6.9.10.23 Q16 Mac OSX Sierra
Input:
convert logo.png -colorspace gray -depth 2 -type truecolor logo_depth8_gray_rgb.bmp
Adding -type truecolor converts the image to RGB, but in gray tones as per the -colorspace gray. And the depth 2 creates only 4 colors in the histogram.
identify -verbose logo_depth8_gray_rgb.bmp
Image:
Filename: logo_depth8_gray_rgb.bmp
Format: BMP (Microsoft Windows bitmap image)
Class: DirectClass
Geometry: 640x480+0+0
Units: PixelsPerCentimeter
Colorspace: sRGB
Type: Grayscale
Base type: Undefined
Endianness: Undefined
Depth: 8/2-bit
Channel depth:
red: 2-bit
green: 2-bit
blue: 2-bit
Channel statistics:
Pixels: 307200
Red:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Green:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Blue:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Image statistics:
Overall:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29928
skewness: -2.38355
entropy: 0.417933
Colors: 4 <--------
Histogram: <--------
12730: (0,0,0) #000000 black
24146: (85,85,85) #555555 srgb(85,85,85)
9602: (170,170,170) #AAAAAA srgb(170,170,170)
260722: (255,255,255) #FFFFFF white
Look at https://en.wikipedia.org/wiki/BMP_file_format#File_structure .
The problem is that you do not specify a color table. According to the wikipedia-article, those are mandatory if the bit depth is less than 8 bit.
Well done on solving the problem. You could consider also making a personal delegate, or custom delegate, for ImageMagick to help automate the process. ImageMagick is able to delegate formats it cannot process itself to delegates, or helpers, such as your 2-bit helper ;-)
Rather than interfere with the system-wide delegates, which probably live in /etc/ImageMagick/delegates.xml, you can make your own in $HOME/.magick/delegates.xml. Yours would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<delegatemap>
<delegate encode="epaper" command="convert "%f" +matte -colors 4 -depth 8 -colorspace gray pgm:- | /usr/local/bin/4_level_gray_4bpp_BMP_converter.py > out.bmp"/>
</delegatemap>
Then if you run:
identify -list delegate
you will see yours listed as a "known" helper.
This all means that you will be able to run commands like:
convert a.png epaper:
and it will do the 2-bit BMP thing automagically.
You can just use this:
convert in.jpg -colorspace gray +matte -colors 2 -depth 1 -resize '640x384>' pgm:- > out.bmp**
I too have this epaper display. After alot of trial and error, I was able to correctly convert the images using ImageMagick using the following command:
convert -verbose INPUT.BMP -resize 300x300 -monochrome -colorspace sRGB -colors 2 -depth 1 BMP3:OUTPUT.BMP
Related
I´m trying to separate a string like the following:
let path = "/Users/user/Downloads/history.csv"
do {
let contents = try NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue )
let rows = contents.components(separatedBy: "\n")
print("contents: \(contents)")
print("rows: \(rows)")
}
catch {
}
I have two files, which are looking almost identical.
From the first file the output is like this:
Output File1:
contents: 2017-07-31 16:29:53,0.10109999,9.74414271,0.98513273,0.15%,42302999779,-0.98513273,9.72952650
2017-07-31 16:29:53,0.10109999,0.25585729,0.02586716,0.25%,42302999779,-0.02586716,0.25521765
rows: ["2017-07-31 16:29:53,0.10109999,9.74414271,0.98513273,0.15%,42302999779,-0.98513273,9.72952650", "2017-07-31 16:29:53,0.10109999,0.25585729,0.02586716,0.25%,42302999779,-0.02586716,0.25521765", "", ""]
Output File2:
contents: 40.75013313,0.00064825,5/18/2017 7:17:01 PM
19.04004820,0.00059900,5/19/2017 9:17:03 PM
rows: ["4\00\0.\07\05\00\01\03\03\01\03\0,\00\0.\00\00\00\06\04\08\02\05\0,\05\0/\01\08\0/\02\00\01\07\0 \07\0:\01\07\0:\00\01\0 \0P\0M\0", "\0", "1\09\0.\00\04\00\00\04\08\02\00\0,\00\0.\00\00\00\05\09\09\00\00\0,\0\05\0/\01\09\0/\02\00\01\07\0 \09\0:\01\07\0:\00\03\0 \0P\0M\0", "\0", "\0", "\0"]
So both files are readable as String because the print(content) is working.
But as soon as the string gets separated, the second file is not readable anymore.
I tried different encodings, but nothing worked. Has anyone an idea, how to force the string to the second file, to remain a readable string?
Your file is apparently UTF-16 (little-endian) encoded:
$ hexdump fullorders4.csv
0000000 4f 00 72 00 64 00 65 00 72 00 55 00 75 00 69 00
0000010 64 00 2c 00 45 00 78 00 63 00 68 00 61 00 6e 00
0000020 67 00 65 00 2c 00 54 00 79 00 70 00 65 00 2c 00
0000030 51 00 75 00 61 00 6e 00 74 00 69 00 74 00 79 00
...
For ASCII characters, the first byte of the UTF-16 encoding is the
ASCII code, and the second byte is zero.
If the file is read as UTF-8 then the zeros are converted to an
ASCII NUL character, that is what you see as \0 in the output.
Therefore specifying the encoding as utf16LittleEndian works
in your case:
let contents = try NSString(contentsOfFile: path, encoding: String.Encoding.utf16LittleEndian.rawValue)
// or:
let contents = try String(contentsOfFile: path, encoding: .utf16LittleEndian)
There is also a method which tries to detect the used encoding
(compare iOS: What's the best way to detect a file's encoding). In Swift that would be
var enc: UInt = 0
let contents = try NSString(contentsOfFile: path, usedEncoding: &enc)
// or:
var enc = String.Encoding.ascii
let contents = try String(contentsOfFile: path, usedEncoding: &enc)
However, in your particular case, that would read the file as UTF-8
again because it is valid UTF-8. Prepending a byte order mark (BOM)
to the file (FF FE for UTF-16 little-endian) would solve that
problem reliably.
I translated two functions in delphi but i don't know if they are right, I need to write the def do_aes_encrypt(key2_t_xor) to know if I am right.
This is what I wrote in delphi:
function key_transform (old_key:string): string;
var
x :integer;
begin
result:='';
for x := 32 downto 0 do
result:= result + chr(ord(old_key[x-1])-( x mod $0C)) ;
end;
function key_xoring ( key2_t :string ; kilo_challenge :string) : string ;
var
i :integer;
begin
result := '';
i:=0 ;
while i <= 28 do begin
result := result + chr(ord(key2_t[i+1]) xor ord(kilo_challenge[3]));
result := result + chr(ord(key2_t[i+2]) xor ord(kilo_challenge[2])) ;
result := result+ chr(ord(key2_t[i+3]) xor ord (kilo_challenge[1])) ;
i := i + 4 ;
end;
end;
This is the original python code:
def key_transform(old_key):
new_key = ''
for x in range(32,0,-1):
new_key += chr(ord(old_key[x-1]) - (x % 0x0C))
return new_key
def key_xoring(key2_t, kilo_challenge):
key2_t_xor = ''
i = 0
while i <= 28:
key2_t_xor += chr(ord(key2_t[i]) ^ ord(kilo_challenge[3]))
key2_t_xor += chr(ord(key2_t[i+1]) ^ ord(kilo_challenge[2]))
key2_t_xor += chr(ord(key2_t[i+2]) ^ ord(kilo_challenge[1]))
key2_t_xor += chr(ord(key2_t[i+3]) ^ ord(kilo_challenge[0]))
i = i + 4
return key2_t_xor
def do_aes_encrypt(key2_t_xor):
plaintext = b''
for k in range(0,16):
plaintext += chr(k)
obj = AES.new(key2_t_xor, AES.MODE_ECB)
return obj.encrypt(plaintext)
/////////////////////////////////////////////////////////////////////////////
{
kilo_challenge = kilo_header[8:12]
chalstring = ":".join("{:02x}".format(ord(k)) for k in kilo_challenge)
key2 = 'qndiakxxuiemdklseqid~a~niq,zjuxl' # if this doesnt work try 'lgowvqnltpvtgogwswqn~n~mtjjjqxro'
kilo_response = do_aes_encrypt(key_xoring(key_transform(key2),kilo_challenge))}
this code is for calculate data line 16 byte to be send as an addition to 32 byte
before
look photo the marked line in blue is what i need to calculate by the 4 byte hex befor marked in porple
and this is the key
key2 = 'qndiakxxuiemdklseqid~a~niq,zjuxl'
in delphi
because python code is working perfect
look to the photo
how it work
this is for lg phones upgrading firmware when i receive the KILOCENT ANSOWER AS THE photo show`s
this below change every time phone connected
||
V
4b 49 4c 4f 43 45 4e 54 ([ac e5 b1 06]) 00 00 00 00 KILOCENT¬å±.....
00 00 00 00 00 00 00 00 30 d4 00 00 b4 b6 b3 b0 ........0Ô..´¶³°
i have to send KILOMETER REQUEST to phone the first and second line is fixed no change but the third i have to change it by the AES ECB MODE encryption look
4b 49 4c 4f 4d 45 54 52 00 00 00 00 02 00 00 00 KILOMETR........
00 00 00 00 10 00 00 00 85 b6 00 00 b4 b6 b3 b0 ........…¶..´¶³°
fc 21 d8 e5 5b aa fd 58 1e 33 58 fd e9 0b 65 38 ü!Øå[ªýX.3Xýé.e8 <==this
and this is old key
key2 = 'qndiakxxuiemdklseqid~a~niq,zjuxl'
How to configure TAO corba server so that IOR string of this server generated from object_to_string is constant?
Each time the IOR string generated from object_to_string changes once server restarts. This is inconvenient since client has to update its cached server IOR string via reloading IOR file or namingservice accessing. As a result, it would be useful if server can generate a constant IOR string, no matter how many times it restarts.
My corba server is based on ACE+TAO and i do remember TAO supports constant IOR string: the IOR string each time it generates are same, and the solution is add some configurations for server. But i could not remember these configurations now.
=============================================
UPDATE:
In ACE_wrappers/TAO/tests/POA/Persistent_ID/server.cpp, i added a new function named testUniqe() which is similiar to creatPOA method. And the update file content is:
void testUniqu(CORBA::ORB_ptr orb_, PortableServer::POA_ptr poa_){
CORBA::PolicyList policies (2);
policies.length (2);
//IOR is the same even it is SYSTEM_ID
policies[0] = poa_->create_id_assignment_policy (PortableServer::USER_ID);
policies[1] = poa_->create_lifespan_policy (PortableServer::PERSISTENT);
PortableServer::POAManager_var poa_manager = poa_->the_POAManager ();
PortableServer::POA_ptr child_poa_ = poa_->create_POA ("childPOA", poa_manager.in (), policies);
// Destroy the policies
for (CORBA::ULong i = 0; i < policies.length (); ++i) {
policies[i]->destroy ();
}
test_i *servant = new test_i (orb_, child_poa_);
PortableServer::ObjectId_var oid = PortableServer::string_to_ObjectId("xushijie");
child_poa_->activate_object_with_id (oid, servant);
PortableServer::ObjectId_var id = poa_->activate_object (servant);
CORBA::Object_var object = poa_->id_to_reference (id.in ());
test_var test = test::_narrow (object.in ());
CORBA::String_var ior = orb_->object_to_string(test.in());
std::cout<<ior.in()<<std::endl;
poa_->the_POAManager()->activate();
orb_->run();
}
int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
try
{
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv);
int result = parse_args (argc, argv);
CORBA::Object_var obj =
orb->resolve_initial_references ("RootPOA");
PortableServer::POA_var root_poa =
PortableServer::POA::_narrow (obj.in ());
PortableServer::POAManager_var poa_manager =
root_poa->the_POAManager ();
testUniqu(orb.in(), root_poa.in());
orb->destroy ();
}
catch (const CORBA::Exception& ex)
{
ex._tao_print_exception ("Exception caught");
return -1;
}
return 0;
}
The problem is that the output server IORs are still different once restart. I also compared this code to the one in Page 412(Advance Corba Programming), but still fail..
///////////////////////////////////
UPDATE:
With "server -ORBListenEndpoints iiop://:1234 > /tmp/ior1", the generated two IORs are:
IOR:010000000d00000049444c3a746573743a312e300000000001000000000000007400000001010200150000007368696a69652d5468696e6b5061642d543431300000d2041b00000014010f0052535453f60054c6f80c000000000001000000010000000002000000000000000800000001000000004f41540100000018000000010000000100010001000000010001050901010000000000
IOR:010000000d00000049444c3a746573743a312e300000000001000000000000007400000001010200150000007368696a69652d5468696e6b5061642d543431300000d2041b00000014010f0052535468f60054da280a000000000001000000010000000002000000000000000800000001000000004f41540100000018000000010000000100010001000000010001050901010000000000
The result for tao_catior for ior1 and ior2:
ior1:
The Byte Order: Little Endian
The Type Id: "IDL:test:1.0"
Number of Profiles in IOR: 1
Profile number: 1
IIOP Version: 1.2
Host Name: **
Port Number: 1234
Object Key len: 27
Object Key as hex:
14 01 0f 00 52 53 54 53 f6 00 54 c6 f8 0c 00 00
00 00 00 01 00 00 00 01 00 00 00
The Object Key as string:
....RSTS..T................
The component <1> ID is 00 (TAG_ORB_TYPE)
ORB Type: 0x54414f00 (TAO)
The component <2> ID is 11 (TAG_CODE_SETS)
Component length: 24
Component byte order: Little Endian
Native CodeSet for char: Hex - 10001 Description - ISO8859_1
Number of CCS for char 1
Conversion Codesets for char are:
1) Hex - 5010001 Description - UTF-8
Native CodeSet for wchar: Hex - 10109 Description - UTF-16
Number of CCS for wchar 0
ecoding an IOR:
//ior2
The Byte Order: Little Endian
The Type Id: "IDL:test:1.0"
Number of Profiles in IOR: 1
Profile number: 1
IIOP Version: 1.2
Host Name: **
Port Number: 1234
Object Key len: 27
Object Key as hex:
14 01 0f 00 52 53 54 68 f6 00 54 da 28 0a 00 00
00 00 00 01 00 00 00 01 00 00 00
The Object Key as string:
....RSTh..T.(..............
The component <1> ID is 00 (TAG_ORB_TYPE)
ORB Type: 0x54414f00 (TAO)
The component <2> ID is 11 (TAG_CODE_SETS)
Component length: 24
Component byte order: Little Endian
Native CodeSet for char: Hex - 10001 Description - ISO8859_1
Number of CCS for char 1
Conversion Codesets for char are:
1) Hex - 5010001 Description - UTF-8
Native CodeSet for wchar: Hex - 10109 Description - UTF-16
Number of CCS for wchar 0
The diff result is:
< 14 01 0f 00 52 53 54 53 f6 00 54 c6 f8 0c 00 00
---
> 14 01 0f 00 52 53 54 68 f6 00 54 da 28 0a 00 00
19c19
< ....RSTS..T................
---
> ....RSTh..T.(..............
Similar diff result is:
< 14 01 0f 00 52 53 54 62 fd 00 54 2c 9a 0e 00 00
---
> 14 01 0f 00 52 53 54 02 fd 00 54 f9 a9 09 00 00
19c19
< ....RSTb..T,...............
---
> ....RST...T................
The difference is in ObjectKey.
============================================
update:
Instead of using above code, i find a better solution with helper TAO_ORB_Manager which is used NamingService and TAO/examples/Simple. TAO_ORB_Manager encapsulates API and generate persistent IORs, as example code in Simple.cpp:
if (this->orb_manager_.init_child_poa (argc, argv, "child_poa") == -1){
CORBA::String_var str =
this->orb_manager_.activate_under_child_poa (servant_name,
this->servant_.in ());
}
This is some description for TAO_ORB_Manager:
class TAO_UTILS_Export TAO_ORB_Manager
{
/**
* Creates a child poa under the root poa with PERSISTENT and
* USER_ID policies. Call this if you want a #a child_poa with the
* above policies, otherwise call init.
*
* #retval -1 Failure
* #retval 0 Success
*/
int init_child_poa (int &argc,
ACE_TCHAR *argv[],
const char *poa_name,
const char *orb_name = 0);
/**
* Precondition: init_child_poa has been called. Activate <servant>
* using the POA <activate_object_with_id> created from the string
* <object_name>. Users should call this to activate objects under
* the child_poa.
*
* #param object_name String name which will be used to create
* an Object ID for the servant.
* #param servant The servant to activate under the child POA.
*
* #return 0 on failure, a string representation of the object ID if
* successful. Caller of this method is responsible for
* memory deallocation of the string.
*/
char *activate_under_child_poa (const char *object_name,
PortableServer::Servant servant);
...................
}
After build, I can get what i want with -ORBListenEndpoints iiop://localhost:2809 option. Thanks #Johnny Willemsen help
You have to create the POA with a persistent lifespan policy, see ACE_wrappers/TAO/tests/POA/Persistent_ID as part of the TAO distribution.
NAL Units start code: 00 00 00 01 X Y
X = IDR Picture NAL Units (25, 45, 65)
X = Non IDR Picture NAL Units (01, 21, 41, 61) ; 01 = b-frames, 41 = p-frames
What does 61 mean?
"01 = b-frames, 41 = p-frames" this is incorrect
Specification is available online for free: http://www.itu.int/rec/T-REC-H.264
Similar question was here just a few days ago: Non IDR Picture NAL Units - 0x21 and 0x61 meaning
I'm writing a program in Objective C to generate a MIDI file. As a test, I'm asking it to write a file which plays one note and stops it a delta tick afterwards.
But I'm trying to open it with Logic and Sibelius, and they both say that the file is corrupted.
Here's the hex readout of the file..
4D 54 68 64 00 00 00 06 00 01 00 01 00 40 - MThd header
4D 54 72 6B 00 00 00 0D - MTrk - with length of 13 as 32bit hex [00 00 00 0D]
81 00 90 48 64 82 00 80 48 64 - the track
delta noteOn delta noteOff
FF 2F 00 - end of file
And here's my routines to write the delta time, and write the note -
- (void) appendNote:(int)note state:(BOOL)on isMelody:(BOOL)melodyNote{ // generate a MIDI note and add it to the 'track' NSData object
char c[3];
if( on ){
c[0] = 0x90;
c[2] = volume;
} else {
c[0] = 0x80;
c[2] = lastVolume;
}
c[1] = note;
[track appendBytes:&c length:3];
}
- (void) writeVarTime:(int)value{ // generate a MIDI delta time and add it to the 'track' NSData object
char c[2];
if( value < 128 ){
c[0] = value;
[track appendBytes:&c length:1];
} else {
c[0] = value/128 | 0x80;
c[1] = value % 128;
[track appendBytes:&c length:2];
}
}
are there any clever MIDI gurus out there who can tell what's wrong with this MIDI file?
The delta time of the EOF event is missing.