Convert MIDI file to list of notes with length and starting time - midi

I am working on a game in Unity that will generate levels from music. I am planning to include simple text files (that don't have to be in a standard format) with the game and parse them to generate the levels. The problem is, I need to be able to convert MIDI files to a text format (preferably not something as complicated as MusicXML). The text files that I will include with the game would ideally consist of a list of notes, each with a length and a starting time (in arbitrary time units). I cannot simply include a music file, as my bullet-hell game will have to precisely time the bullets with the notes.
I do not care what programming language this is in, as this code will not be included with the game. Also, I have never worked with MIDI before in any form. I am happy to use any library and/or free program for this.
Thank you in advance for your help!

You can use any open-source library for parsing MIDI file and convert its data to text file in your desired format. For example, with the DryWetMIDI you can use this code:
public static void ConvertMidiToText(string midiFilePath, string textFilePath)
{
var midiFile = MidiFile.Read(midiFilePath);
File.WriteAllLines(textFilePath,
midiFile.GetNotes()
.Select(n => $"{n.NoteNumber} {n.Time} {n.Length}"));
}
ConvertMidiToText method will produce text files like this:
37 0 480
37 960 480
37 1920 480
37 2400 480
70 2640 192
where first number is note number (60 = C4), second one is starting time in MIDI ticks and the third one is length in MIDI ticks.
You can even write time and length in hours:minutes:seconds format. This code
public static void ConvertMidiToText(string midiFilePath, string textFilePath)
{
var midiFile = MidiFile.Read(midiFilePath);
TempoMap tempoMap = midiFile.GetTempoMap();
File.WriteAllLines(textFilePath,
midiFile.GetNotes()
.Select(n => $"{n.NoteNumber} {n.TimeAs<MetricTimeSpan>(tempoMap)} {n.LengthAs<MetricTimeSpan>(tempoMap)}"));
}
will produce text like this:
37 00:00:00 00:00:00.4800000
37 00:00:00.9600000 00:00:00.4800000
37 00:00:01.9200000 00:00:00.4800000
37 00:00:02.4000000 00:00:00.4800000
70 00:00:02.6400000 00:00:00.1920000

Related

can open dbc edit - selecting non sequential bytes for 16 bit data

I am trying to write a .dbc file for a can-open data log (example of one of lines I am trying to use below)
Time 884.163000, ID:2a1, Data Bytes (0)7b (1)00 (2)95 (3)68 (4)e5 (5)8e (6)49 (7)54
I have written .dbc files using both motorola and intel endianness using candb++ covering 16 bit data over 2 bytes, but this has always been with sequential bytes, ie- (2),(3) or (5),(6).
The bytes I need to use for the particular data in the above example are (3) and (7) in intel format (54,68 in this case). I have written a .dbc for just byte 3 shown in the snip below.
BO_ 673 Rig_Pressure: 4 Vector__XXX
SG_ Pressure_Multiplex M : 15|8#0+ (1,0) [0|0] "" Vector__XXX
SG_ Pressure m0 : 31|8#0- (0.45,58) [0.399999999999999|115.15] "Bar" Vector__XXX
I am asking if there is a way to modify the text file (or use cabdb++) to specify each bit or pick 2 non sequential bytes in the .dbc, something like modifying the bit start something like
31|8#0- to 31|8#0- 63|8#0-
I am far from a computer programmer, I much prefer GUI based programs and am only starting out in learning python, so please be gentle!!!
Thank you!

Time Signature Meta Message in MIDI

I am working on a MIDI project using mido library in Python. I see in the manual a meta message for time signature with value: notated_32nd_notes_per_beat which has a default value of 8.
<meta message time_signature numerator=4 denominator=4 clocks_per_click=24 notated_32nd_notes_per_beat=8 time=0>
Which makes sense. However, can I define it like:
<meta message time_signature numerator=4 denominator=4 clocks_per_click=24 notated_32nd_notes_per_beat=32 time=0>
Does this increase the display resolution when shown in a score/typesetting software? What is the usage of this please?
time_signature (0x58) meta message in midi files
The file header specifies the number of ticks per beat, and the tempo messages specify the length of a beat, in microseconds. These value are needed to correctly play back the file.
The last field of the time signature message specifies how the tick values in the MIDI file relates to notes in a score. It does not affect at what time events are sent (so a pure playback program will ignore this message), but how notes are displayed.
For example, if the header says there are 100 ticks per beat, and the time signature has the default of 8 32th notes per beat, then a note-on/note-off pair with a distance of 100 ticks is displayed as a quarter note. If you change the time signature to 32 32th notes per beat, then a length of 100 ticks corresponds to a whole note.

Avformat cannot seek to beginning of file

I need to quickly seek thru H.264 encoded video stream in MP4 container. I am using libav to decode frames, so I stumbled upon avformat_seek_file() method.
My problem is, assuming H.264 stream begins with keyframe, when I seek to timestamp 0 (regardless of time_base), I should be at the beggining of the stream. But Im not. I usually get few seconds into video. Also, if I seek to, for example 10 seconds, I usually get around 12 or so. Is it possible for keyframes to be so "rare"? It seems that AVSEEK_FLAG_ANY has no impact on seek result. Tested on multiple FullHD H.264 MP4 videos.
Code:
unsigned long seekTo = 0;
//Doesen´t actually matter for 0 since it will be also 0
seekTo = av_rescale_q(seekTo, AVRational{1, AV_TIME_BASE}, pFormatCtx->streams[videoStream]->time_base);
int result = avformat_seek_file(pFormatCtx, videoStream, INT_FAST64_MIN, seekTo, seekTo, AVSEEK_FLAG_ANY);
avcodec_flush_buffers(pCodecCtx);
Try using av_seek_frame instead. Read here for some gotchas about using that and seeking around.
My problem is, assuming H.264 stream begins with keyframe, when I seek to timestamp 0 (regardless of time_base), I should be at the beggining of the stream
Note that some files can have their first keyframe at a negative DTS, e.g. you need to seek to timestamp -1 or something like this.
You can set the flag inside AVFMT_SEEK_TO_PTS into AVInputFormat::flags before opening the AVFormatContext to use PTS which will be 0-based.

CGPDFScannerPopString returning strange result

I finally got some sort of pdf scanner to work. It reads into the callback functions without a problem, but when I try to NSLog the result from a CGPDFScannerPopString I get a result like this:
ˆ ˛˝ # ˜˜˜ #˜' ˜˜˜ "˜ '˜˜ " ' ˜˜
No string to be found here...
Any ideas of what it can be?
This is my callback function:
static void op_Tj (CGPDFScannerRef s, void *info)
{
CGPDFStringRef string;
if (!CGPDFScannerPopString(s, &string))
return;
NSLog(#"string: %#", (__bridge NSString *)CGPDFStringCopyTextString(string));
}
Thanks already!
Edit: Example PDF
You should be aware that the CGPDFStringRef is not a ASCII string or something similar at all. Cf. http://developer.apple.com/library/mac/documentation/graphicsimaging/Reference/CGPDFString/Reference/reference.html --- it is a "series of bytes—unsigned integer values in the range 0 to 255" which have to be interpreted according to the latest PDF reference.
The PDF reference in turn will tell you that the interpretation of the bytes depends on the font used, and while ASCII-like interpretations are common in case of European languages, they are not mandatory, and in case of Asian languages where font subset embedding is very common, the interpretation may look random.
CGPDFStringCopyTextString tries to interpret those bytes accordingly, but there does not have to be a sensible interpretation as a regular string.
EDIT Inspection of the sample PDF Ron supplied showed that in case of this sample indeed the encoding of the font in object 3 0 (which is dominant on most pages of the document) is not a standard encoding but instead:
<</Type/Encoding
/Differences[0/.notdef/C/O/V/E/R/space/slash/H/L/F/underscore/W/B/five/eight/four
/zero/two/six/D/one/period/three/Z/I/N/G/U/S/T/colon/seven/A/M/P/Y
/plus/nine/X/hyphen/i/s/p/a/t/c/h/n/f/o/K/greater/equal/l/m/y/J/Q
/parenleft/parenright/comma/dollar/ampersand/d/r/v/b/e/u/w/k/g/x/bar
/quotesingle/asterisk/q/question/percent]
>>
Looking at the top of the first document page
COVER / HLF_CWEB_58408485 / 58408485 / 26DEC12 10.30.22Z
BRIEFING INCLUDES FOLLOWING FLIGHTS:
26DEC12 OR0337 EHAM0630 MUVR1710 PHOYE VSM+2/8 179
NEXT FLIGHTS OF AIRCRAFT:
26DEC12 OR0338 MUVR1830 MMUN1940 PHOYE VSM+2/8 213
26DEC12 OR0338 MMUN2105 EHAM0655 PHOYE GPT+2/7 263
27DEC12 OR0365 EHAM0900 TNCB1930 PHOYE BAH+1/8 272
27DEC12 OR0366 TNCB2030 TNCC2110 PHOYE BAH+1/8 250
27DEC12 OR0366 TNCC2250 EHAM0835 PHOYE ASD+1/8 199
that encoding seems to have been created by dealing out the next number starting from one for the next required glyph. This obviously results in a highly individualistic encoding...
That being said the font object does include both an /Encoding entry and a /ToUnicode entry. Thus, if the method CGPDFStringCopyTextString was given a reference to the font here and really tried, it would easily be able to correctly translate those bytes into the corresponding text. That it doesn't achieve anything decent, seems to indicate that it simply does not have the information which font to interpret the bytes for --- I don't assume it doesn't try...
For accurate text extraction, therefore, you have to interpret the bytes in the CGPDFStringRef yourself using the information of the the font in the content stream. If you don't want to do that from scratch, you might be interested in PDFKitten, a framework for extracting data from PDFs in iOS. While it is not yet perfect (some font structures can baffle it), it is a good starting point.

How can I store raw data with respect to time and sort it?

due to Internet communication i could have two (or more) ASCII files in RINEX format (GPS ASCII format) of the same data period, which i would like to merge to one file.
Each data set (epoch) contain more then one line (in this example 19 lines). I would like to merge those files, where it could be that they in some parts overlap each other.
here is an example of RINEX epoch data set:
09 2 21 12 59 59.9000000 0 9G31G23G11G13G32G17G14G20G19
23152606.238 121667768.06047 94806069.43545 23152606.540 23152606.521
1262.605 43.750 31.500
22765313.352 119632547.53447 93220179.18745 22765312.252 22765311.072
3252.769 46.250 32.250
20798168.896 109295128.07748 85165036.96747 20798168.642 20798168.578
-2252.493 52.750 43.250
25363206.177 133284559.23845
3776.403 32.750
20350616.203 106943239.96448 83332404.31147 20350615.386 20350616.499
-929.443 51.000 44.500
21994260.713 115580595.93348 90062809.84446 21994260.826 21994260.114
416.327 49.500 38.250
23964108.994 125932271.15846 98129049.02843 23964107.689 23964107.603
-3561.500 39.250 20.250
20225257.452 106284459.64448 82819085.85247 20225256.341 20225256.964
956.944 52.750 45.250
25623383.323 134651746.21445 104923415.17742 25623386.202 25623384.504
-3991.096 34.250 12.250
The first line contains the time info and below are the raw data for each GPS satellite.
My idea was to open each file separate and stored the raw data in some kind of array relative to time. Each time i read new epoch, i ask my array if i already have something with time so and so, and if not i place the raw data there.
My question is how to store the raw data with respect to time, since it is not one line but something dynamic that could always change.
If you have better idea, please share it with me.
Regards
To store the raw data with respect to time, I would:
Encode the time as a number (# of seconds since Unix "epoch time" or since some arbitrary start time - use microseconds instead of seconds depending on what RINEX time precision is).
Store the raw data as an array (data for each line is 1 array element - stored either as a string, an arrayref of words or a hash of values).
Store the reference to that array as a value in a hash, with the key being the time-encoded-as-a-number.