How to convert midi files to keypresses (in Python)? - python

I'm trying to read a MIDI file and then convert each note (midi number) to a simulated keypress on the keyboard (A,f,h,J,t...).
I'm able to read any MIDI file with the python-midi library like this:
pattern = midi.read_midifile("example.mid")
and I can also simulate keypresses with pywin32 like this:
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys(key)
But I have no idea on how to actually convert midi numbers (that are in midi files) to keypresses.
To be more precise I'm trying to convert MIDI numbers to notes and then notes to keypresses according to virtualpiano.net (61-key) so that the program would play that piano by pressing the corresponding button on the keyboard (you can press key assist in settings of the piano to see which key is what button)
Of course I would also have to wait between keypresses but that's easy enough.
Any help is appreciated. (Windows 10 64-bit (32-bit Python 2.7))

If you take a look at the midi module that you are using, you will see that there are some constants that can be used to convert notes to their MIDI number and vice versa.
>>> import midi
>>> midi.C_0 # note C octave 0
0
>>> midi.G_3 # G octave 3
43
>>> midi.Gs_4 # G# octave 4
56
>>> midi.A_8 # A octave 8
105
>>> midi.NOTE_VALUE_MAP_SHARP[0]
C_0
>>> midi.NOTE_VALUE_MAP_SHARP[56]
Gs_4
>>> midi.NOTE_VALUE_MAP_SHARP[105]
A_8
Opening a MIDI file with read_midifile() returns a Pattern object which looks like this (taken from the examples):
>>> midi.read_midifile('example.mid')
midi.Pattern(format=1, resolution=220, tracks=\
[midi.Track(\
[midi.NoteOnEvent(tick=0, channel=0, data=[43, 20]),
midi.NoteOffEvent(tick=100, channel=0, data=[43, 0]),
midi.EndOfTrackEvent(tick=1, data=[])])])
The NoteOnEvent contains timing, MIDI number/pitch and velocity which you can retrieve:
>>> on = midi.NoteOnEvent(tick=0, channel=0, data=[43, 20])
>>> on.pitch
43
>>> midi.NOTE_VALUE_MAP_SHARP[on.pitch]
'G_3'
Now all of that is interesting, but you don't really need to convert the MIDI number to a note, you just need to convert it to the keyboard key for that note as used by http://virtualpiano.net/.
Middle C is equal to MIDI 60 and this note corresponds to the 25th key on the virtualpiano keyboard which is activated by pressing the letter t. The next note, Cs_5, is MIDI 61 which is uppercase T (<shift>-t). From there you can work out the mapping for the MIDI numbers to the supported virtualpiano keys; it's this:
midi_to_vk = (
[None]*36 +
list('1!2#34$5%6^78*9(0qQwWeErtTyYuiIoOpPasSdDfgGhHjJklLzZxcCvVbBnm') +
[None]*31
)
The next problem that you will face is sending the key events. Note that in MIDI multiple notes can be played simultaneously, or can overlap in time. This means that you might need to be able to send more than one key press event at the same time.
I don't think that you can handle the velocity using a computer keyboard. There is also the issue of timing, but you said that that's not a problem for you.

Check this question first. This is general idea on how to simulate keypresses, it may look a lot but it's just a big list of keys. Then to convert midi to keyboard buttons you would create a dictionary mapping between notes and Keyboard buttons.

I understand that what you really need is a way to convert a midi note number to a note in standard notation.
Here are some elements from Wikipedia:
... GM specifies that note number 69 plays A440, which in turn fixes middle C as note number 60 ...
(from MIDI - General MIDI
Converting from midi note number (d) to frequency (f) is given by the following formula: f = 2 (d-69)/12 * 440 Hz
(from MIDI Tuning Standard - Frequency values)
And finally from C (musical note) - Designation by octave
Scientific designation |Octave name | Frequency (Hz) | Other names
C4 | One-lined | 261.626 | Middle C
So C4 is midi note 69, and midi notes are separated with a semitone(*). As you have 12 semitones in one octave, you will get C5 at midi note 81, and the famous A440 (midi note 69) is A4 in scientific notation.
As an example for the table
Midi note | Scientific notation
60 | C4
62 | D4
64 | E4
65 | F4
67 | G4
69 | A4
70 | B4
71 | C5
And you would get F♯4 at midi 66...
(*) it is musically what is called equal temperament. Midi allows for finer pitch definition but it would be far beyond this answer.

Related

SPIDEV on raspberry pi for TI DAC8568 not behaving as expected

I have a Texas Instruments DAC8568 in their BOOST breakout board package. The DAC8568 is an 8 channel, 16bit DAC with SPI interface. The BOOST package has headers to connect it to my raspberry pi, and it has LEDs connected to the output voltage so you can easily check to see if your code is doing what you think it does. Links to the BOOST package and datasheet of the DAC8568 are in my python code below.
I have the package wired to the raspberry Pi with the 3.3V supply, the 5V supply (needed for LEDs), and ground. The DACs SCLK goes to Pi SCLK, DAC /SYNC (which is really chip select) goes to Pi CE1, DAC /LDAC goes to Pi Gnd, and DAC MOSI goes to Pi MOSI. I do not wire the DACs /CLR, but I can physically hook it to ground to reset the chip if I need to.
I believe my wiring is good, because I can light the LEDs with either a python script or from the terminal using: sudo echo -ne "\xXX\xXX\xXX\xXX" > /dev/spidev0.1
I learned the terminal trick from this video: https://www.youtube.com/watch?v=iwzXh2V1SP4
My problem though is that the LEDs are not lighting as I would expect them to according to the data sheet. I should be lighting A, but instead I light B. I should light B but instead I light D, etc. I have tried to make sense of it all and can dim the LEDs and turn on new ones, but never in the way I would really expect it to work according to the datasheet.
Below is my python script. In the comments I mentioned where in the datasheet I am looking for the bits to send. I am very new to working with analog components and am not a EE, so maybe I am not doing the timing correctly, or making some other silly error. Perhaps someone can look at the datasheet and see my error without having to actually have the chip in hand. Thanks for the help!
# -*- coding: utf-8 -*-
"""
Created on Sat Jul 8 16:33:05 2017
#author: pi
for texas instruments BOOST DAC8568
for BOOST schematic showing LEDs http://www.ti.com/tool/boost-dac8568
for DAC8568 datasheet: http://www.ti.com/product/dac8568
"""
import spidev
import time
spi = spidev.SpiDev() #create spi object
spi.open(0,1) #open spi port 0, device (CS) 1
#spi.bits_per_word = 8 does not seem to matter
#spi.max_speed_hz = 50000000 #does not seem to matter
#you have to power the DAC, you can write to the buffer and later power on if you like
power_up = spi.xfer2([0x04, 0x00, 0x00, 0xFF]) #p.37 Table11 in datasheet: powers all DACS
voltage_write = spi.xfer2([0x00, 0x0F, 0xFF, 0xFF ]) #p.35 Table11 in datasheet supposed write A--but lights B
voltage_write = spi.xfer2([0x00, 0x1F, 0xFF, 0xFF ]) #supposed write B--but lights D
voltage_write = spi.xfer2([0x00, 0x2F, 0xFF, 0xFF ]) #supposed write C--but lights F
voltage_write = spi.xfer2([0x00, 0x3F, 0xFF, 0xFF ]) #supposed write D--but lights H
voltage_write = spi.xfer2([0x00, 0x4F, 0xFF, 0xFF ]) #supposed write E--but does nothing
spi.close()
Note for future readers, the power up needs to power on the internal reference which is
power_up = spi.xfer2([0x08, 0x00, 0x00, 0xFF]) #(p.37 datasheet
Comment: the bits are shifted. ... how ... compensate for the shift or eliminate the shift?
This could be the case, if the SPIDIV.mode is not in sync with the DAC.
DAC Datasheet Page 6/7:
This input is the frame synchronization signal for the input data.
When SYNC goes low, it enables the input shiftregister,
and data are sampled on subsequent SYNC falling clock edges.
The DAC output updates following the 32nd clock.
Reference: Clock polarity and phase
According to the above and the Timing Diagram I come to the conclusion that SPDIV.mode == 2
is the right.
Check the actual SPDIV.mode
Change to SPDIV.mode = 2
I can confirm your used Values by reading Table 11 Page 35.
Write to Input Register - DAC Channel X
My Example set Feature Bits = 0
3 2 1
10987654321098765432109876543210
RXXXCCCCAAAADDDDDDDDDDDDDDDDFFFF
A = 32-bit[00000000000011111111111111110000]:0xffff0 ('0x00', '0x0f', '0xff', '0xf0')
3 2 1
10987654321098765432109876543210
RXXXCCCCAAAADDDDDDDDDDDDDDDDFFFF
B = 32-bit[00000000000111111111111111110000]:0x1ffff0 ('0x00', '0x1f', '0xff', '0xf0')
Page 33:
DB31(MSB) is the first bit that is loaded into the DAC shift register and must be always set to '0'.
The wireing seems straight forward and simple, but worth to doublecheck.
Code Snippet from testing:
def writeDAC(command, address, data, feature=0x0):
address = ord(address) - ord('A')
b1 = command
b2 = address << 4 | data >> 12 # 4 address Bits and 4 MSB data Bits
b3 = data >> 4 # middle 8 Bits of data
b4 = 0xF0 & (data << 4) >> 8 | feature # 4 data Bits and feature Bits
voltage_write = spi.xfer2([b1, b2, b3, b4])
# Usage:
# Write Command=0 Channel=B Data=0xFFFF Default Features=0x0
writeDAC(0, 'B', 0xFFFF)

Raspberry pi adc: MPC3001 using python and spidev

I am trying to use a MPC3001 ADC with the Raspberry pi over SPI using Python.
While doing so I am getting some strange results:
The code I am using:
import sys
import spidev
spi = spidev.SpiDev()
spi.open(0,0)
def readAdc(channel):
r = spi.xfer2([1, 8 + channel << 4, 0])
return ((r[1]&3) << 8) + r[2]
while True:
print readAdc(0)
time.sleep(0.5)
running the script above, while measuring the center point of a voltage divider, yields a random switching between 2 values: 504 and 1016.
Since 504 is the value I would expect to be correct, in combination with the binary representation of the two results;
504 --> 00111111000
1016 --> 01111111000
I assume I am accidentally 'creating' a 1 somewhere.
Can someone point me in the right direction.
Thanks in advance
BTW: Is it me, or is there no decent documentation for the spidev lib?
In the data sheet figure 5-2 it shows what the issue is. The data returned from the device, bit by bit, looks like this (where X = don't care, and Bx is bit number x of the data; B0 is LSB and B9 MSB):
BYTE0: X X 0 B9 B8 B7 B6 B5
BYTE1: B4 B3 B2 B1 B0 B1 B2 B3
BYTE2: B4 B5 B6 B7 B8 B9 X X
If you change the return statement to this:
return ((r[0] & 0x1F) << 5) | ((r[1] >> 3) & 0x1F)
it might work.
But if I were you I wouldn't trust me (I no longer have a hardware setup where I could test anything). I think it's best to print the individual values of the bytes in r and ponder the result until it starts to make sense. Be aware that some SPI implementations let you read in the data on either the rising or the falling edge. If you have that wrong your data will never make sense.
Another clue here is that the three LSBs of an A/D converter reading shouldn't be consistently 000, for reading after reading. There is almost always noise in the least significant bit, at least, if not two or three bits.
BTW I agree with you about spidev. I switched to quick2wire and then things went much more smoothly. It's better written and much better documented.

How to send NRPN messages with pygame midi

I'm writing a program that will read incoming cc messages from a device that can only send cc's and send it to another device as an nrpn message. I know how to send cc messages from pygame but I can't wrap my mind around how to send nrpn's. I looked at the Vriareon code and I don't see anywhere in there were it even accesses midi. Can anyone give an example of how this is done?
Thank you!
NRPN messages are CC messages.
However, NRPN numbers are distinct from CC numbers. The MIDI specification says:
Controller number 6 (Data Entry), in conjunction with Controller numbers 96 (Data Increment), 97 (Data Decrement), 98 (Non-Registered Parameter Number LSB), 99 (Non-Registered Parameter Number MSB), 100 (Registered Parameter Number LSB), and 101 (Registered Parameter Number MSB), extend the number of controllers available via MIDI. Parameter data is transferred by first selecting the parameter number to be edited using controllers 98 and 99 or 100 and 101, and then adjusting the data value for that parameter using controller number 6, 96, or 97.
To change a controller like volume (7), you would send a single message:
B0 07 xx
To change an NRPN, you would select the NRPN first:
B0 63 mm
B0 62 ll
And then change the currently selected NRPN with the data entry controller:
B0 06 mm
B0 26 ll (optional, for 14-bit values)
So setting NRPN 0:1 to value 42 could be done with:
self.midi_out.write_short(0xb0, 0x63, 0)
self.midi_out.write_short(0xb0, 0x62, 1)
self.midi_out.write_short(0xb0, 0x06, 42)

Converting assembly instructions BL and B to binary

I am trying to understand how a binary containing binary codes get converted to assembly instruction.
For example here is a example output from objdump for an ARM based application:
00008420 <main>:
8420: e92d4800 push {fp, lr}
8424: e28db004 add fp, sp, #4
8428: e24dd008 sub sp, sp, #8
842c: e59f2054 ldr r2, [pc, #84] ; 8488 <main+0x68>
8430: e24b300c sub r3, fp, #12
8434: e1a00002 mov r0, r2
8438: e1a01003 mov r1, r3
843c: ebffffc6 bl 835c <__isoc99_scanf#plt>
8440: e3a03000 mov r3, #0
8444: e50b3008 str r3, [fp, #-8]
8448: ea000006 b 8468 <main+0x48>
844c: e51b3008 ldr r3, [fp, #-8]
8450: e2833001 add r3, r3, #1
8454: e50b3008 str r3, [fp, #-8]
8458: e59f302c ldr r3, [pc, #44] ; 848c <main+0x6c>
845c: e1a00003 mov r0, r3
8460: e51b1008 ldr r1, [fp, #-8]
8464: ebffffb3 bl 8338 <printf#plt>
8468: e51b300c ldr r3, [fp, #-12]
846c: e51b2008 ldr r2, [fp, #-8]
8470: e1520003 cmp r2, r3
8474: bafffff4 blt 844c <main+0x2c>
8478: e3a03000 mov r3, #0
847c: e1a00003 mov r0, r3
8480: e24bd004 sub sp, fp, #4
8484: e8bd8800 pop {fp, pc}
8488: 00008500 .word 0x00008500
848c: 00008504 .word 0x00008504
as you can see in the offset 8464, the binary code ebffffb3 get converted to bl 8338. I want to understand it.
The explicit reason to do it is because I want to add additional regex for instructions existing in the following python code:
[b"[\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}\x47", 2, 2], # bx reg
[b"[\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0]{1}\x47", 2, 2], # blx reg
[b"[\x00-\xff]{1}\xbd", 2, 2] # pop {,pc}
As you can see the regex for an bx instruction in the binary is "\x00\x08\x10\x18\x20\x28\x30\x38\x40\x48\x70]{1}\x47" and for blx it is "\x80\x88\x90\x98\xa0\xa8\xb0\xb8\xc0\xc8\xf0". Now I want to add two more instructions B and BL (these are ARM instructions) but I have no idea how to convert the instruction to the similar binary code.
(The source code coming from ROPGadget in github. )
I am trying to understand how a binary containing binary codes get converted to assembly instruction.
Aside: All traditional CPU hardware uses binary logic using some standard transistor configurations for implementing NOT, NOR, NAND, etc. From these few logic gates, you can implement many more sophisticated device and logic using combinations of the logic elements.
So, all CPUs will extract bit fields (a few bit positions, but not necessarily adjacent) and determine which type of instruction it is. Other bit fields will give parameters to the particular opcode.
In 'C', this convert to some mask and compare operations where you extract the bits to be examined and then see if the bit pattern is equal. The specific implementation for the GNU tools (binutils) is arm-dis.c.
This sourceforge project is one source of information, although there are others (including the arm-dis.c file).
|31..28|27..25| 24|23 .. 0|
+------+------+---+----------+
|cond | 101 | L | offset |
+------+------+---+----------+
The only constant part is the '101'. Your python reg-ex looks like hexadecimal. The leading nibble is a condition which if true the instruction will take; otherwise it is like a no op. There was a never (leading hex 'F') condition in very old ARM CPU documentation; it has been deprecated to extend the instruction set. So the leading nibble (four bits) can be ignored and then look for either '1010b' or 0xa (for the branch) and '1011b' or 0xb (for the bl or branch and link).
For example, arm-dis.c has,
{ARM_FEATURE_CORE_LOW (ARM_EXT_V1),
0x0a000000, 0x0e000000, "b%24'l%c\t%b"},
That said, the b and bl instructions are not that useful for ROP as they do not have register arguments, so you can not alter the control flow. Normally, you would just arrange to have the control flow directly in your ROP gadget instead of trying to get to them through a jump.
The ARM version of b Rn is mov pc, rN; but there are many other rich constructs such as add with shift and using ldr with tables of pointer, etc. Afaik, the ROPGadget was detecting these when I ran it on an ARM glibc.
Quoting from https://www.ic.unicamp.br/~ranido/mc404/arm/arm-instructionset.pdf
Branch instructions contain a signed 2’s complement 24 bit offset.
This is shifted left two bits, sign extended to 32 bits, and added to
the PC. The instruction can therefore specify a branch of +/-
32Mbytes. The branch offset must take account of the prefetch
operation, which causes the PC to be 2 words (8 bytes) ahead of the
current instruction. Branches beyond +/- 32Mbytes must use an offset
or absolute destination which has been previously loaded into a
register. In this case the PC should be manually saved in R14 if a
Branch with Link type operation is required.
So let's look at your branch example
8464: ebffffb3 bl 8338 <printf#plt>
The processor logic takes the 24-bit offset ffffb3 and multiplies it by 4 (that's efficiently coded because of 4-byte alignment). It then adds this offset to the current instruction's Program Counter + 8. This gives the sum:
ffffffb3 * 4
--------
fffffecc +
8464 +
8 +
--------
8338 QED

Python Wave byte data

I'm trying to read the data from a .wav file.
import wave
wr = wave.open("~/01 Road.wav", 'r')
# sample width is 2 bytes
# number of channels is 2
wave_data = wr.readframes(1)
print(wave_data)
This gives:
b'\x00\x00\x00\x00'
Which is the "first frame" of the song. These 4 bytes obviously correspond to the (2 channels * 2 byte sample width) bytes per frame, but what does each byte correspond to?
In particular, I'm trying to convert it to a mono amplitude signal.
If you want to understand what the 'frame' is you will have to read the standard of the wave file format. For instance: https://web.archive.org/web/20140221054954/http://home.roadrunner.com/~jgglatt/tech/wave.htm
From that document:
The sample points that are meant to be "played" ie, sent to a Digital to Analog Converter(DAC) simultaneously are collectively called a sample frame. In the example of our stereo waveform, every two sample points makes up another sample frame. This is illustrated below for that stereo example.
sample sample sample
frame 0 frame 1 frame N
_____ _____ _____ _____ _____ _____
| ch1 | ch2 | ch1 | ch2 | . . . | ch1 | ch2 |
|_____|_____|_____|_____| |_____|_____|
_____
| | = one sample point
|_____|
To convert to mono you could do something like this,
import wave
def stereo_to_mono(hex1, hex2):
"""average two hex string samples"""
return hex((ord(hex1) + ord(hex2))/2)
wr = wave.open('piano2.wav','r')
nchannels, sampwidth, framerate, nframes, comptype, compname = wr.getparams()
ww = wave.open('piano_mono.wav','wb')
ww.setparams((1,sampwidth,framerate,nframes,comptype,compname))
frames = wr.readframes(wr.getnframes()-1)
new_frames = ''
for (s1, s2) in zip(frames[0::2],frames[1::2]):
new_frames += stereo_to_mono(s1,s2)[2:].zfill(2).decode('hex')
ww.writeframes(new_frames)
There is no clear-cut way to go from stereo to mono. You could just drop one channel. Above, I am averaging the channels. It all depends on your application.
For wav file IO I prefer to use scipy. It is perhaps overkill for reading a wav file, but generally after reading the wav it is easier to do downstream processing.
import scipy.io.wavfile
fs1, y1 = scipy.io.wavfile.read(filename)
From here the data y1, will be N samples long, and will have Z columns where each column corresponds to a channel. To convert to a mono wav file you don't say how you'd like to do that conversion. You can take the average, or whatever else you'd like. For average use
monoChannel = y1.mean(axis=1)
As a direct answer to your question: two bytes make one 16-bit integer value in the "usual" way, given by the explicit formula: value = ord(data[0]) + 256 * ord(data[1]). But using the struct module is a better way to decode (and later reencode) such multibyte integers:
import struct
print(struct.unpack("HH", b"\x00\x00\x00\x00"))
# -> gives a 2-tuple of integers, here (0, 0)
or, if we want a signed 16-bit integer (which I think is the case in .wav files), use "hh" instead of "HH". (I leave to you the task of figuring out how exactly two bytes can encode an integer value from -32768 to 32767 :-)
Another way to convert 2 bytes into an int16, use numpy.fromstring(). Here's an example:
audio_sample is from a wav file.
>>> audio_sample[0:8]
b'\x8b\xff\xe1\xff\x92\xffn\xff'
>>> x = np.fromstring(audio_sample, np.int16)
>>> x[0:4]
array([-117, -31, -110, -146], dtype=int16)
You can use np.tobytes to convert back to bytes

Categories