#!/usr/local/bin/python3

from _ListLib import _sortLOL, _partDerange, _partDerangeMap
from MIDI import midi2score, midi2ms_score, score2midi
import copy
import random
import sys
import os


# -----------------------------------------------------------------------------
# returns the duration of a midi sequence on ms
# -----------------------------------------------------------------------------
def _midiLen(midiInput):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	midiListSorted = _sortLOL(midiList, 0)
	return midiListSorted[-1][0]+midiListSorted[-1][1]


# *****************************************************************************	
# --------------------------LIST <--> MIDI FILES----------------------------- #
# *****************************************************************************	

# -----------------------------------------------------------------------------
# converts a midi file to list fo midi events in format of [[start_time1, duration1, pitch1, velocity1], [start_time1, duration1, pitch1, velocity1]...]
# -----------------------------------------------------------------------------
def _midi2list(filename):
	
	file=open(filename, "rb").read()
	#the list made by midi2score from midi.py
	directFromFile = midi2ms_score(file)
	
	#removing the first element of the list directFromFile
	allEvents = directFromFile[1]
	
	#filtering out from directFromFile only the note events
	#output list:
	output = []
	for i in allEvents:
		if i[0] == 'note':
			output.append(i[1:3]+i[4:6])	
	# sorting the output list by the start times
	output = _sortLOL(output, 0)
	return output


# -----------------------------------------------------------------------------
# converts a list in the format of [[start_time1, duration1, pitch1, velocity1], [start_time1, duration1, pitch1, velocity1]...] to a midi file. as input it takes the list and the file name to be saved 
# -----------------------------------------------------------------------------
def _list2midi(L, filename):
	#creates a score according to MIDI library format
	# sorting the input list by start times
	LCopy = copy.deepcopy(L)
	LCopy = _sortLOL(LCopy, 0)
	
	iter1 = []
	for i in LCopy:
		i.insert(0, 'note')
		i.insert(3, 0)
		iter1.append(i)
	
	score = [500, [['time_signature', 0, 4, 2, 24, 8], ['set_tempo', 0, 500000]] + LCopy]
	
	midiFile = open(filename, 'wb')
	midiFile.write(score2midi(score))
	midiFile.close
	return

# *****************************************************************************	
# ---------------------------------ANALYSIS---------------------------------- #
# *****************************************************************************	

# -----------------------------------------------------------------------------
# calculates the density of a midi file: notes / sec. output: a list of lists with times and densities from attack to attack
# INPUT: midi file name or midi list
# OUTPUT: list with densities
# -----------------------------------------------------------------------------
def _midiDens(midiInput):
	if type(midiInput).__name__ == 'list':
		L = copy.deepcopy(midiInput)
	else:
		L = _midi2list(midiInput)
	
	# creating from the file a list with midi evets
	L = _sortLOL(L, 0)
	lenL = len(L)
	output = []
	# the list with start times
	sT = []

	for i in L:
		sT.append(i[0])

	# deleting duplicates in the sT list:
	sT_noDup = []
	for i in sT:
		alreadyThere = False
		for j in sT_noDup:
			if (i == j):
				alreadyThere = True
		if alreadyThere == False:
			sT_noDup.append(i)
	
	# the list: [start time, how much attacks with this start time in the midi list]
	sT_amount = []
	for i in sT_noDup:
		sT_amount.append([i, sT.count(i)])
	
	lenST_amount = len(sT_amount)
	
	for count in range(lenST_amount):
		if ( count != (lenST_amount - 1)):
			output.append( [ sT_amount[count][0], ( sT_amount[count][1] / ( sT_amount[count+1][0] - sT_amount[count][0] ) ) ] )
		
		else:
			output.append( [ sT_amount[count][0], ( sT_amount[count][1] / L[len(L)-1][1] ) ] )
	
	return output

# *****************************************************************************	
# -------------------------TRANSFORMING STUFF-------------------------------- #
# *****************************************************************************	

# -----------------------------------------------------------------------------
# multiplies a parameter of some N (in percents) randomly chosen notes of the sequence with the coeff K in respect to the middle line M
# INPUT: midi file name or midi list, N, K, M, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiCoeff(midiInput, N, K, M, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	
	indxToChange = random.sample(indxs, round(nOfNotes*N/100))
	
	if p == 'p':
		for i in indxToChange:
			curPitch = midiList[i][2]
			newPitch = K*curPitch + M*(1-K)
			if (newPitch > 108):
				midiList[i][2] = 108
			elif (newPitch < 21):
				midiList[i][2] = 21
			else:
				midiList[i][2] = newPitch	
	
	if p == 'v':
		for i in indxToChange:
			curVel = midiList[i][3]
			newVel = round(K*curVel + M*(1-K))
			if (newVel > 127):
				midiList[i][3] = 127
			else:
				midiList[i][3] = newVel
	
	if p == 'd':
		for i in indxToChange:
			curDur = midiList[i][1]
			newDur = K*curDur + M*(1-K)
			if (newDur < 1):
				midiList[i][1] = 1
			else:
				midiList[i][1] = round(newDur)
				
	if p == 'a':
		for i in indxToChange:
			curAtt = midiList[i][0]
			newAtt = K*curAtt + M*(1-K)
			if (newAtt <= 0):
				pass
			else:
				midiList[i][0] = round(newAtt)	
	
	outFileN = '_midiCoeff(midiInput, '+str(N)+', '+str(K)+', '+str(M)+', '+p + ')'
	midiList = _sortLOL(midiList, 0)
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	
	return midiList

# -----------------------------------------------------------------------------
# multiplies a parameter of the elements with indexes from the map list of the sequence with the coeff K in respect to the middle line M
# INPUT: midi file name or midi list, map, K, M, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiCoeffMap(midiInput, map, K, M, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	
	indxToChange = map
	
	if p == 'p':
		for i in indxToChange:
			curPitch = midiList[i][2]
			newPitch = K*curPitch + M*(1-K)
			if (newPitch > 108):
				midiList[i][2] = 108
			elif (newPitch < 21):
				midiList[i][2] = 21
			else:
				midiList[i][2] = newPitch	
	
	if p == 'v':
		for i in indxToChange:
			curVel = midiList[i][3]
			newVel = round(K*curVel + M*(1-K))
			if (newVel > 127):
				midiList[i][3] = 127
			else:
				midiList[i][3] = newVel
	
	if p == 'd':
		for i in indxToChange:
			curDur = midiList[i][1]
			newDur = K*curDur + M*(1-K)
			if (newDur < 10):
				midiList[i][1] = 10
			else:
				midiList[i][1] = round(newDur)
				
	if p == 'a':
		for i in indxToChange:
			curAtt = midiList[i][0]
			newAtt = K*curAtt + M*(1-K)
			if (newAtt <= 0):
				pass
			else:
				midiList[i][0] = round(newAtt)	
	
	outFileN = '_midiCoeffMap(midiInput, '+str(K)+', '+str(M)+', '+p + ')'
	midiList = _sortLOL(midiList, 0)
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	
	return midiList


# -----------------------------------------------------------------------------
# multiplies a parameter of some N (in percents) randomly chosen notes of the sequence with the random coeff between K1 and K2 in respect to the random middle line between M1 and M2. the generation of the coeff and the middle line occurs for each midi event.
# INPUT: midi file name or midi list, N, K1, K2, M1, M2, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiRandCoeff(midiInput, N, K1, K2, M1, M2, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	
	indxToChange = random.sample(indxs, round(nOfNotes*N/100))
	
	if p == 'p':
		for i in indxToChange:
			curPitch = midiList[i][2]
			print('K1: ', K1)
			print('K2: ', K2)
			# generating new random K and M
			K = random.uniform(K1, K2)
			print('chosen K (pitch)  = ', K)
			M = round(random.uniform(M1, M2))
			newPitch = K*curPitch + M*(1-K)
			if (newPitch > 108):
				midiList[i][2] = 108
			elif (newPitch < 21):
				midiList[i][2] = 21
			else:
				midiList[i][2] = newPitch	
	
	if p == 'v':
		for i in indxToChange:
			curVel = midiList[i][3]
			print('K1: ', K1)
			print('K2: ', K2)
			# generating new random K and M
			K = random.uniform(K1, K2)
			print('chosen K (vel) = ', K)
			M = round(random.uniform(M1, M2))
			newVel = K*curVel + M*(1-K)
			if (newVel > 127):
				midiList[i][3] = 127
			elif (newVel<30):
				midiList[i][3] = 30
			else:
				midiList[i][3] = round(newVel)
	
	if p == 'd':
		for i in indxToChange:
			curDur = midiList[i][1]
			print('K1: ', K1)
			print('K2: ', K2)
			# generating new random K and M
			K = random.uniform(K1, K2)
			print('chosen K (dur) = ', K)
			M = round(random.uniform(M1, M2))
			newDur = K*curDur + M*(1-K)
			if (newDur < 10):
				midiList[i][1] = 10
			else:
				midiList[i][1] = round(newDur)
			
	if p == 'a':
		for i in indxToChange:
			curAtt = midiList[i][0]
			print('K1: ', K1)
			print('K2: ', K2)
			# generating new random K and M
			K = random.uniform(K1, K2)
			print('chosen K (attacks) = ', K)
			M = round(random.uniform(M1, M2))
			newAtt = K*curAtt + M*(1-K)
			if (newAtt <= 0):
				pass
			else:
				midiList[i][0] = round(newAtt)	
	outFileN = '_midiRandCoeff(midiInput, '+str(N)+', '+str(K1)+', '+str(K2)+', '+str(M1)+', '+str(M2)+', ' + p + ')'
	midiList = _sortLOL(midiList, 0)
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	
	return midiList


# -----------------------------------------------------------------------------
# multiplies a parameter of some N (in percents) randomly chosen notes of the sequence with the random coeff between K1 and K2 or between K3 and K4 in respect to the random middle line between M1 and M2 or between M3 and M4. the generation of the coeff and the middle line occurs for each midi event.
# INPUT: midi file name or midi list, N, K1, K2, K3, K4, M1, M2, M3, M4, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiRandCoeff2(midiInput, N, K1, K2, K3, K4, M1, M2, M3, M4, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	
	indxToChange = random.sample(indxs, round(nOfNotes*N/100))
	
	if p == 'p':
		for i in indxToChange:
			curPitch = midiList[i][2]
			curArea = random.choice([0, 1])
			# generating new random K and M
			print('K1: ', K1)
			print('K2: ', K2)
			print('K3: ', K3)
			print('K4: ', K4)
			if curArea == 0:
				K = random.uniform(K1, K2)
				print('chosen K (pitch) = ', K)
				M = round(random.uniform(M1, M2))
			else: 
				K = random.uniform(K3, K4)
				print('chosen K (pitch) = ', K)
				M = round(random.uniform(M3, M4))
			newPitch = K*curPitch + M*(1-K)
			if (newPitch > 108):
				midiList[i][2] = 108
			elif (newPitch < 21):
				midiList[i][2] = 21
			else:
				midiList[i][2] = newPitch	
	
	if p == 'v':
		for i in indxToChange:
			curVel = midiList[i][3]
			curArea = random.choice([0, 1])
			print('K1: ', K1)
			print('K2: ', K2)
			print('K3: ', K3)
			print('K4: ', K4)
			# generating new random K and M
			if curArea == 0:
				K = random.uniform(K1, K2)
				print('chosen K (vel) = ', K)
				M = round(random.uniform(M1, M2))
			else: 
				K = random.uniform(K3, K4)
				print('chosen K (vel) = ', K)
				M = round(random.uniform(M3, M4))
			newVel = K*curVel + M*(1-K)
			if (newVel > 127):
				midiList[i][3] = 127
			elif (newVel<30):
				midiList[i][3] = 30
			else:
				midiList[i][3] = round(newVel)
	
	if p == 'd':
		for i in indxToChange:
			curDur = midiList[i][1]
			curArea = random.choice([0, 1])
			print('K1: ', K1)
			print('K2: ', K2)
			print('K3: ', K3)
			print('K4: ', K4)
			# generating new random K and M
			if curArea == 0:
				K = random.uniform(K1, K2)
				print('chosen K (dur) = ', K)
				M = round(random.uniform(M1, M2))
			else: 
				K = random.uniform(K3, K4)
				print('chosen K (dur) = ', K)
				M = round(random.uniform(M3, M4))
			newDur = K*curDur + M*(1-K)
			if (newDur < 10):
				midiList[i][1] = 10
			else:
				midiList[i][1] = round(newDur)
			
	if p == 'a':
		for i in indxToChange:
			curAtt = midiList[i][0]
			curArea = random.choice([0, 1])
			print('K1: ', K1)
			print('K2: ', K2)
			print('K3: ', K3)
			print('K4: ', K4)
			# generating new random K and M
			if curArea == 0:
				K = random.uniform(K1, K2)
				print('chosen K (attacks) = ', K)
				M = round(random.uniform(M1, M2))
			else: 
				K = random.uniform(K3, K4)
				print('chosen K (attacks) = ', K)
				M = round(random.uniform(M3, M4))
			newAtt = K*curAtt + M*(1-K)
			if (newAtt <= 0):
				pass
			else:
				midiList[i][0] = round(newAtt)	
	outFileN = '_midiRandCoeff2(midiInput, '+str(N)+', '+str(K1)+', '+str(K2)+', '+str(K3)+', '+str(K4)+', '+str(M1)+', '+str(M2)+', '+str(M3)+', '+str(M4)+', ' + p + ')'
	midiList = _sortLOL(midiList, 0)
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	
	return midiList



# -----------------------------------------------------------------------------
# multiplies a parameter of the elements with indexes from the map list of the sequence with the random coeff between K1 and K2 in respect to the random middle line between M1 and M2. the generation of the coeff and the middle line occurs for each midi event.
# INPUT: midi file name or midi list, map, K1, K2, M1, M2, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiRandCoeffMap(midiInput, map, K1, K2, M1, M2, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	
	indxToChange = map
	
	if p == 'p':
		for i in indxToChange:
			curPitch = midiList[i][2]
			# generating new random K and M
			K = random.uniform(K1, K2)
			M = random.uniform(M1, M2)
			newPitch = K*curPitch + M*(1-K)
			if (newPitch > 108):
				midiList[i][2] = 108
			elif (newPitch < 21):
				midiList[i][2] = 21
			else:
				midiList[i][2] = newPitch	
	
	if p == 'v':
		for i in indxToChange:
			curVel = midiList[i][3]
			# generating new random K and M
			K = random.uniform(K1, K2)
			M = random.uniform(M1, M2)
			newVel = K*curVel + M*(1-K)
			if (newVel > 127):
				midiList[i][3] = 127
			else:
				midiList[i][3] = round(newVel)
	
	if p == 'd':
		for i in indxToChange:
			curDur = midiList[i][1]
			# generating new random K and M
			K = random.uniform(K1, K2)
			M = random.uniform(M1, M2)
			newDur = K*curDur + M*(1-K)
			if (newDur < 10):
				midiList[i][1] = 10
			else:
				midiList[i][1] = round(newDur)
			
	if p == 'a':
		for i in indxToChange:
			curAtt = midiList[i][0]
			# generating new random K and M
			K = random.uniform(K1, K2)
			M = random.uniform(M1, M2)
			newAtt = K*curAtt + M*(1-K)
			if (newAtt <= 0):
				pass
			else:
				midiList[i][0] = round(newAtt)	
	outFileN = '_midiRandCoeffMap(midiInput, '+str(K1)+', '+str(K2)+', '+str(M1)+', '+str(M2)+', ' + p + ')'
	midiList = _sortLOL(midiList, 0)
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	
	return midiList


# -----------------------------------------------------------------------------
# adds a random value between V1 and V2 to the parameter of some N (in percents) randomly chosen notes of the sequence.
# INPUT: midi file name or midi list, N, V1, V2, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# the function doesn't make the sequence longer!!
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiAddVal(midiInput, N, V1, V2, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	
	indxToChange = random.sample(indxs, round(nOfNotes*N/100))
	indxToChange.sort()
	if p == 'p':
		for i in indxToChange:
			curPitch = midiList[i][2]
			V = random.uniform(V1, V2)
			curSign = random.choice([1,-1])
			newPitch = curPitch + V*curSign
			if (newPitch > 108):
				midiList[i][2] = 108
			elif (newPitch < 21):
				midiList[i][2] = 21
			else:
				midiList[i][2] = newPitch	
	
	if p == 'v':
		for i in indxToChange:
			curVel = midiList[i][3]
			V = random.uniform(V1, V2)
			curSign = random.choice([1,-1])
			newVel = round(curVel + V*curSign)
			if (newVel > 127):
				midiList[i][3] = 127
			elif (newVel < 10):
				midiList[i][3] = 10
			else:
				midiList[i][3] = round(newVel)
	
	if p == 'd':
		for i in indxToChange:
			curDur = midiList[i][1]
			V = random.uniform(V1, V2)
			curSign = random.choice([1,-1])
			newDur = curDur + V*curSign
			if (newDur < 1):
				midiList[i][1] = 1
			
			elif (midiList[i][0]+newDur) > _midiLen(midiList):
				midiList[i][1] = _midiLen(midiList)-midiList[i][0]
			
			else:
				midiList[i][1] = round(newDur)

	if p == 'd+':
		for i in indxToChange:
			curDur = midiList[i][1]
			V = random.uniform(V1, V2)
			curSign = 1
			newDur = curDur + V*curSign
			if (newDur < 1):
				midiList[i][1] = 1
			
			elif (midiList[i][0]+newDur) > _midiLen(midiList):
				midiList[i][1] = _midiLen(midiList)-midiList[i][0]
			
			else:
				midiList[i][1] = round(newDur)
	
	if p == 'd-':
		for i in indxToChange:
			curDur = midiList[i][1]
			V = random.uniform(V1, V2)
			curSign = -1
			newDur = curDur + V*curSign
			if (newDur < 1):
				midiList[i][1] = 1
			
			elif (midiList[i][0]+newDur) > _midiLen(midiList):
				midiList[i][1] = _midiLen(midiList)-midiList[i][0]
			
			else:
				midiList[i][1] = round(newDur)
	
	
	
	
	
	
	
	
	
	
	
	if p == 'a':
		for i in indxToChange:
			curAtt = midiList[i][0]
			while True:
				V = random.uniform(V1, V2)
				curSign = random.choice([1,-1])
				newAtt = curAtt + V*curSign
				if newAtt >=0:
					break
			
			if (newAtt > _midiLen(midiList)):
				midiList[i][0] = _midiLen(midiList)-midiList[i][1]
			else:
				midiList[i][0] = round(newAtt)
					
	outFileN = '_midiAddVal(midiInput, '+str(N)+', '+str(V1)+', '+str(V2)+', ' + p + ')'
	midiList = _sortLOL(midiList, 0)
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	return midiList

# -----------------------------------------------------------------------------
# adds a random value between V1 and V2 to the parameter of the elements with indexes from the map list of the sequence.
# INPUT: midi file name or midi list, map, V1, V2, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# the function doesn't make the sequence longer!!
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiAddValMap(midiInput, map, V1, V2, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	
	indxToChange = map
	indxToChange.sort()
	if p == 'p':
		for i in indxToChange:
			curPitch = midiList[i][2]
			V = random.uniform(V1, V2)
			curSign = random.choice([1,-1])
			newPitch = curPitch + V*curSign
			if (newPitch > 108):
				midiList[i][2] = 108
			elif (newPitch < 21):
				midiList[i][2] = 21
			else:
				midiList[i][2] = newPitch	
	
	if p == 'v':
		for i in indxToChange:
			curVel = midiList[i][3]
			V = random.uniform(V1, V2)
			curSign = random.choice([1,-1])
			newVel = round(curVel + V*curSign)
			if (newVel > 127):
				midiList[i][3] = 127
			elif (newVel <= 0):
				midiList[i][3] = 10
			else:
				midiList[i][3] = round(newVel)
	
	if p == 'd':
		for i in indxToChange:
			curDur = midiList[i][1]
			V = random.uniform(V1, V2)
			curSign = random.choice([1,-1])
			newDur = curDur + V*curSign
			if (newDur < 10):
				midiList[i][1] = 10
			
			elif (midiList[i][0]+newDur) > _midiLen(midiList):
				midiList[i][1] = _midiLen(midiList)-midiList[i][0]
			
			else:
				midiList[i][1] = round(newDur)

	if p == 'a':
		for i in indxToChange:
			curAtt = midiList[i][0]
			while True:
				V = random.uniform(V1, V2)
				curSign = random.choice([1,-1])
				newAtt = curAtt + V*curSign
				if newAtt >=0:
					break
			
			if (newAtt > _midiLen(midiList)):
				midiList[i][0] = _midiLen(midiList)-midiList[i][1]
			else:
				midiList[i][0] = round(newAtt)
					
	outFileN = '_midiAddValMap(midiInput, '+str(V1)+', '+str(V2)+', ' + p + ')'
	midiList = _sortLOL(midiList, 0)
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	return midiList
	

# -----------------------------------------------------------------------------
# replaces a parameter of some N (in percents) randomly chosen notes of the sequence with the new value V. 
# INPUT: midi file name or midi list, N, V, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _midiReplace(midiInput, N, V, p, writeMid ='y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	indxs = list(range(nOfNotes))
	indxToChange = random.sample(indxs, round(nOfNotes*N/100))
	
	if p == 'p':
		for i in indxToChange:
			midiList[i][2] = V	
	if p == 'v':
		for i in indxToChange:
			midiList[i][3] = V
	
	if p == 'd':
		for i in indxToChange:
			midiList[i][1] = V
	if p == 'a':
		for i in indxToChange:
			midiList[i][0] = V
	
	outFileN = '_midiReplace'
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	
	return midiList


# -----------------------------------------------------------------------------
# replaces a parameter of some N (in percents) randomly chosen notes of the sequence with the new value V. 
# INPUT: midi file name or midi list, N, V, p - midi parameter, 'p', 'v', 'd' or 'a'. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: changed midi list
# -----------------------------------------------------------------------------
def _addOffset(midiInput, offSet, writeMid = 'y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
	else:
		midiList = _midi2list(midiInput)
	
	nOfNotes = len(midiList)
	
	for i in range(nOfNotes):
		midiList[i][0] = midiList[i][0] + offSet
	
	outFileN = '_addOffset'
	if writeMid == 'y':
		# creating the midi file with the midi list in it			
		_list2midi(midiList, outFileN+'.mid')
	
	if writeTxt == 'y':
		# creating the text file with the midi list in it
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(midiList))
		outTextFile.close
	
	return midiList

# -----------------------------------------------------------------------------
# merges horizontally midi sequences to one midi sequence. 
# INPUT: list of midi file names or list of midi lists. [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. 
# if midiInput is a list of midi lists: CREATES THE NEW LIST
# WHEN CALLING THE FUNCTION YOU HAVE TO PASS THE KEY ARGUMENTS, IF YOU DON'T WANT TO USE DEFAULTS like this: _midiMerge(blabla, writeMid='n', writeTxt = 'n').
# OUTPUT: merged midi list
# -----------------------------------------------------------------------------
def _midiMerge(*argv, writeMid='y', writeTxt = 'y'):
	# the list with all input sequences as elements
	allInput = []
	
	if type(argv[0]).__name__ == 'list':
		for i in argv:
			allInput.append(i)
	else:
		for i in argv:
			allInput.append(_midi2list(i))
	
	# the output list with all midi events from all sequences to be merged
	preOutput = []
	
	for i in allInput:
		#print(i)
		for j in i:
			preOutput.append(j)
	
	output = _sortLOL(preOutput, 0)		
	
	outFileN = '_midiMerge'
	if writeMid=='y':
		# saving merged input as midi file
		_list2midi(output, outFileN+'.mid')
	
	if writeTxt=='y':
		# save as merged input as text file
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(output))
		outTextFile.close
	return output



	

# *****************************************************************************	
# -------------------------PERMUTING STUFF----------------------------------- #
# *****************************************************************************	

# -----------------------------------------------------------------------------	
# permutes (derangements) some N randomly attacks (in percents) of the midi sequence. if two parameters are given, permutations occur dependently: pitch+velocity, duration+velocity, pitch+duration are permuted together as one gestalt.
# INPUT: midi file name or midi list, N, p - parameter: 'a', 'd', 'p', 'v' , 'pv', 'dv', 'pd', [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. 
# if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: midi list, midi files
# -----------------------------------------------------------------------------
def _midiPermutDep(midiInput, N, p, writeMid = 'y', writeTxt = 'y' ):
	
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
		inFile = 'noFile'
	else:
		midiList = _midi2list(midiInput)
		inFile = os.path.basename(midiFileName)

	nOfNotes = len(midiList)
	output = []
	nToChange = round(nOfNotes*N/100)
	
	if p == 'a':
		attacks = []
		for i in midiList:
			attacks.append(i[0])
		(attPerm, curSum, maxSum) =  _partDerange(attacks, nToChange)
		for i in range(len(attPerm)):
			output.append([attPerm[i]]+midiList[i][1:4])
		outFileN = 'perm_'+p.swapcase()+'_'+inFile+'_'+str(N)
	
	if p == 'p':
		pitches = []
		for i in midiList:
			pitches.append(i[2])
		(pitchPerm, curSum, maxSum) =  _partDerange(pitches, nToChange)
		for i in range(len(pitchPerm)):
			output.append(midiList[i][0:2] + [pitchPerm[i]] + [midiList[i][3]])
		outFileN = 'perm_'+p.swapcase()+'_'+inFile+'_'+str(N)

	if p == 'd':
		durs = []
		for i in midiList:
			durs.append(i[1])
		(dursPerm, curSum, maxSum) =  _partDerange(durs, nToChange)
		for i in range(len(dursPerm)):
			output.append([midiList[i][0]] + [dursPerm[i]] + midiList[i][2:4])	
		outFileN = 'perm_'+p.swapcase()+'_'+inFile+'_'+str(N)
	
	if p == 'v':
		vels = []
		for i in midiList:
			vels.append(i[3])
		(velsPerm, curSum, maxSum) =  _partDerange(vels, nToChange)
		for i in range(len(velsPerm)):
			output.append(midiList[i][0:3] + [velsPerm[i]])	
		outFileN = 'perm_'+p.swapcase()+'_'+inFile+'_'+str(N)
	
	# dependently
	if p == 'dv':
		ind = list(range(0, nOfNotes))
		(indPerm, curSum, maxSum) =  _partDerange(ind, nToChange)
		for i in range(len(indPerm)):
			output.append([midiList[i][0]] + [midiList[indPerm[i]][1]] + [midiList[i][2]] +  [midiList[indPerm[i]][3]])	
		outFileN = 'depPerm_'+p.swapcase()+'_'+inFile+'_'+str(N)
	
	# dependently
	if p == 'pd':
		ind = list(range(0, nOfNotes))
		(indPerm, curSum, maxSum) =  _partDerange(ind, nToChange)
		for i in range(len(indPerm)):
			output.append([midiList[i][0]] + [midiList[indPerm[i]][1]] + [midiList[indPerm[i]][2]] +  [midiList[i][3]])
		outFileN = 'depPerm_'+p.swapcase()+'_'+inFile+'_'+str(N)
	
	# dependently
	if p == 'pv':
		ind = list(range(0, nOfNotes))
		(indPerm, curSum, maxSum) =  _partDerange(ind, nToChange)
		for i in range(len(indPerm)):
			output.append([midiList[i][0]] + [midiList[i][1]] + [midiList[indPerm[i]][2]] +  [midiList[indPerm[i]][3]])
		outFileN = 'depPerm_'+p.swapcase()+'_'+inFile+'_'+str(N)
		
	output = _sortLOL(output, 0)
	
	if writeMid == 'y':
		_list2midi(output, outFileN+'.mid')
	
	if writeTxt=='y':
		# save as merged input as text file
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(output))
		outTextFile.close
	
	return output

# -----------------------------------------------------------------------------	
# permutes (derangements) the elements with indexes from the map list of the midi sequence. if two parameters are given, permutations occur dependently: pitch+velocity, duration+velocity, pitch+duration are permuted together as one gestalt.
# INPUT: midi file name or midi list, map, p - parameter: 'a', 'd', 'p', 'v' , 'pv', 'dv', 'pd', [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. 
# if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: midi list, midi files
# -----------------------------------------------------------------------------
def _midiPermutDepMap(midiInput, map, p, writeMid = 'y', writeTxt = 'y' ):
	
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
		inFile = 'noFile'
	else:
		midiList = _midi2list(midiInput)
		inFile = os.path.basename(midiFileName)

	nOfNotes = len(midiList)
	output = []
	if p == 'a':
		attacks = []
		for i in midiList:
			attacks.append(i[0])
		(attPerm, curSum, maxSum) =  _partDerangeMap(attacks, map)
		for i in range(len(attPerm)):
			output.append([attPerm[i]]+midiList[i][1:4])
		outFileN = 'permMap_'+p.swapcase()+'_'+inFile
	
	if p == 'p':
		pitches = []
		for i in midiList:
			pitches.append(i[2])
		(pitchPerm, curSum, maxSum) =  _partDerangeMap(pitches, map)
		for i in range(len(pitchPerm)):
			output.append(midiList[i][0:2] + [pitchPerm[i]] + [midiList[i][3]])
		outFileN = 'permMap_'+p.swapcase()+'_'+inFile

	if p == 'd':
		durs = []
		for i in midiList:
			durs.append(i[1])
		(dursPerm, curSum, maxSum) =  _partDerangeMap(durs, map)
		for i in range(len(dursPerm)):
			output.append([midiList[i][0]] + [dursPerm[i]] + midiList[i][2:4])	
		outFileN = 'permMap_'+p.swapcase()+'_'+inFile
	
	if p == 'v':
		vels = []
		for i in midiList:
			vels.append(i[3])
		(velsPerm, curSum, maxSum) =  _partDerangeMap(vels, map)
		for i in range(len(velsPerm)):
			output.append(midiList[i][0:3] + [velsPerm[i]])	
		outFileN = 'permMap_'+p.swapcase()+'_'+inFile
	
	# dependently
	if p == 'dv':
		ind = list(range(0, nOfNotes))
		(indPerm, curSum, maxSum) =  _partDerangeMap(ind, map)
		for i in range(len(indPerm)):
			output.append([midiList[i][0]] + [midiList[indPerm[i]][1]] + [midiList[i][2]] +  [midiList[indPerm[i]][3]])	
		outFileN = 'depPermMap_'+p.swapcase()+'_'+inFile
	
	# dependently
	if p == 'pd':
		ind = list(range(0, nOfNotes))
		(indPerm, curSum, maxSum) =  _partDerangeMap(ind, map)
		for i in range(len(indPerm)):
			output.append([midiList[i][0]] + [midiList[indPerm[i]][1]] + [midiList[indPerm[i]][2]] +  [midiList[i][3]])
		outFileN = 'depPermMap_'+p.swapcase()+'_'+inFile
	
	# dependently
	if p == 'pv':
		ind = list(range(0, nOfNotes))
		(indPerm, curSum, maxSum) =  _partDerangeMap(ind, map)
		for i in range(len(indPerm)):
			output.append([midiList[i][0]] + [midiList[i][1]] + [midiList[indPerm[i]][2]] +  [midiList[indPerm[i]][3]])
		outFileN = 'depPermMap_'+p.swapcase()+'_'+inFile
		
	output = _sortLOL(output, 0)
	
	if writeMid == 'y':
		_list2midi(output, outFileN+'.mid')
	
	if writeTxt=='y':
		# save as merged input as text file
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(output))
		outTextFile.close
	
	return output


# -----------------------------------------------------------------------------	
# permutes (derangements) some N1 randomly attacks (in percents) with parameter 1 and some N2 randomly attacks (in percents) with parameter 2 of the midi sequence. permutations occur independently from each other.
# INPUT: midi file name or midi list, N1, N2, p - parameter: 'a', 'd', 'p', 'v' , 'pv', 'dv', 'pd', [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. 
# if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: midi list, midi file
# -----------------------------------------------------------------------------
def _midiPermutIndep(midiList, N1, N2, p, writeMid = 'y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
		inFile = 'noFile'
	else:
		midiList = _midi2list(midiInput)
		inFile = os.path.basename(midiFileName)
	
	nOfNotes = len(midiList)
	output = []

	# number of notes for independently changes of the first parameter
	nToChange1 = round(nOfNotes*N1/100)
	# number of notes for independently changes of the first parameter
	nToChange2 = round(nOfNotes*N2/100)
	
	if p == 'dv': 
		# list with all indexes
		allInd = list(range(0, nOfNotes))
		
		# choosing some nToChange1 indexes for duration permutations
		dInd = random.sample(allInd, nToChange1)
		dInd.sort()
		# making a list with indexes with deranged duration indexes: making a derangement od allInd according to the map - dind
		(dIndDer, curSum, maxSum) = _partDerangeMap(allInd, dInd)
		
		# making a list with indexes with deranged velocities indexes: making a derangement od allInd according to the map - vInd. with while loop checking, if some note of the deranged output version the same parameters as the input midi list
		while True:
			equal = False
			vInd = sample(allInd, nToChange2)
			vInd.sort()
		# for velocities
			(vIndDer, curSum, maxSum) = _partDerangeMap(allInd, vInd)
			for i in dInd:
				if dIndDer[i] == vIndDer[i]:
					equal = True
			for i in vInd:
				if dIndDer[i] == vIndDer[i]:
					equal = True
			if equal == False:
				break
		# building the output sequence
		for i in allInd:
			new = [midiList[i][0]]+[midiList[dIndDer[i]][1]]+[midiList[i][2]]+[midiList[vIndDer[i]][3]]
			output.append(new)

		
	if p == 'pd': 
		# list with all indexes
		allInd = list(range(0, nOfNotes))
		# choosing some nToChange1 indexes for pitch permutations
		pInd = random.sample(allInd, nToChange1)
		pInd.sort()
		# making a list with indexes with deranged pitch indexes: making a derangement od allInd according to the map - pind
		(pIndDer, curSum, maxSum) = _partDerangeMap(allInd, pInd)
		
		# making a list with indexes with deranged durations indexes: making a derangement od allInd according to the map - dInd. with while-loop checking, if some note of the deranged output version the same parameters as the input midi list
		while True:
			equal = False
			dInd = random.sample(allInd, nToChange2)
			dInd.sort()
		# for durations
			(dIndDer, curSum, maxSum) = _partDerangeMap(allInd, dInd)
			for i in pInd:
				if pIndDer[i] == dIndDer[i]:
					equal = True
			for i in dInd:
				if pIndDer[i] == dIndDer[i]:
					equal = True
			if equal == False:
				break
		# building the output sequence
		for i in allInd:
			new = [midiList[i][0]]+[midiList[dIndDer[i]][1]]+[midiList[pIndDer[i]][2]]+[midiList[i][3]]
			output.append(new)

	
	if p == 'pv': 
		# list with all indexes
		allInd = list(range(0, nOfNotes))
		# choosing some nToChange1 indexes for pitch permutations
		pInd = random.sample(allInd, nToChange1)
		pInd.sort()
		# making a list with indexes with deranged pitch indexes: making a derangement od allInd according to the map - pind
		(pIndDer, curSum, maxSum) = _partDerangeMap(allInd, pInd)
		
		# making a list with indexes with deranged velocities indexes: making a derangement od allInd according to the map - vInd. with while-loop checking, if some note of the deranged output version the same parameters as the input midi list
		while True:
			equal = False
			vInd = random.sample(allInd, nToChange2)
			vInd.sort()
		# for durations
			(vIndDer, curSum, maxSum) = _partDerangeMap(allInd, vInd)
			for i in vInd:
				if pIndDer[i] == vIndDer[i]:
					equal = True
			for i in vInd:
				if pIndDer[i] == vIndDer[i]:
					equal = True
			if equal == False:
				break
		# building the output sequence
		for i in allInd:
			new = midiList[i][0:2]+[midiList[pIndDer[i]][2]]+[midiList[vIndDer[i]][3]]
			output.append(new)
		
	output = _sortLOL(output, 0)
	
	outFileN = 'indepPerm_'+p.swapcase()+'_'+inFile+'_'+str(N1) + '-' + str(N2)
	
	if writeMid == 'y':
		_list2midi(output, outFileN+'.mid')
	
	if writeTxt=='y':
		# save as merged input as text file
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(output))
		outTextFile.close
	return output

# -----------------------------------------------------------------------------	
# permutes (derangements) independently parameter 1 of the elements with indexes from the map1 and parameter 2 of the elements with indexes from the map2 list the midi sequence. permutations occur independently from each other.
# INPUT: midi file name or midi list, N1, N2, p - parameter: 'a', 'd', 'p', 'v' , 'pv', 'dv', 'pd', [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. 
# if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: midi list, midi file
# -----------------------------------------------------------------------------
def _midiPermutIndepMap(midiList, map1, map2, p, writeMid = 'y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
		inFile = 'noFile'
	else:
		midiList = _midi2list(midiInput)
		inFile = os.path.basename(midiFileName)
	
	nOfNotes = len(midiList)
	output = []
	
	if p == 'dv': 
		# list with all indexes
		allInd = list(range(0, nOfNotes))
		
		dInd = map1
		dInd.sort()
		# making a list with indexes with deranged duration indexes: making a derangement od allInd according to the map - dind
		(dIndDer, curSum, maxSum) = _partDerangeMap(allInd, dInd)
		
		# making a list with indexes with deranged velocities indexes: making a derangement od allInd according to the map - vInd. with while loop checking, if some note of the deranged output version the same parameters as the input midi list
		while True:
			equal = False
			vInd = map2
			vInd.sort()
		# for velocities
			(vIndDer, curSum, maxSum) = _partDerangeMap(allInd, vInd)
			for i in dInd:
				if dIndDer[i] == vIndDer[i]:
					equal = True
			for i in vInd:
				if dIndDer[i] == vIndDer[i]:
					equal = True
			if equal == False:
				break
		# building the output sequence
		for i in allInd:
			new = [midiList[i][0]]+[midiList[dIndDer[i]][1]]+[midiList[i][2]]+[midiList[vIndDer[i]][3]]
			output.append(new)

		
	if p == 'pd': 
		# list with all indexes
		allInd = list(range(0, nOfNotes))
		pInd = map1
		pInd.sort()
		# making a list with indexes with deranged pitch indexes: making a derangement od allInd according to the map - pind
		(pIndDer, curSum, maxSum) = _partDerangeMap(allInd, pInd)
		
		# making a list with indexes with deranged durations indexes: making a derangement od allInd according to the map - dInd. with while-loop checking, if some note of the deranged output version the same parameters as the input midi list
		while True:
			equal = False
			dInd = map2
			dInd.sort()
		# for durations
			(dIndDer, curSum, maxSum) = _partDerangeMap(allInd, dInd)
			for i in pInd:
				if pIndDer[i] == dIndDer[i]:
					equal = True
			for i in dInd:
				if pIndDer[i] == dIndDer[i]:
					equal = True
			if equal == False:
				break
		# building the output sequence
		for i in allInd:
			new = [midiList[i][0]]+[midiList[dIndDer[i]][1]]+[midiList[pIndDer[i]][2]]+[midiList[i][3]]
			output.append(new)

	
	if p == 'pv': 
		# list with all indexes
		allInd = list(range(0, nOfNotes))
		pInd = map1
		pInd.sort()
		# making a list with indexes with deranged pitch indexes: making a derangement od allInd according to the map - pind
		(pIndDer, curSum, maxSum) = _partDerangeMap(allInd, pInd)
		
		# making a list with indexes with deranged velocities indexes: making a derangement od allInd according to the map - vInd. with while-loop checking, if some note of the deranged output version the same parameters as the input midi list
		while True:
			equal = False
			vInd = map2
			vInd.sort()
		# for durations
			(vIndDer, curSum, maxSum) = _partDerangeMap(allInd, vInd)
			for i in vInd:
				if pIndDer[i] == vIndDer[i]:
					equal = True
			for i in vInd:
				if pIndDer[i] == vIndDer[i]:
					equal = True
			if equal == False:
				break
		# building the output sequence
		for i in allInd:
			new = midiList[i][0:2]+[midiList[pIndDer[i]][2]]+[midiList[vIndDer[i]][3]]
			output.append(new)
		
	output = _sortLOL(output, 0)
	
	outFileN = 'indepPermMap_'+p.swapcase()+'_'+inFile
	
	if writeMid == 'y':
		_list2midi(output, outFileN+'.mid')
	
	if writeTxt=='y':
		# save as merged input as text file
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(output))
		outTextFile.close
	return output




# -----------------------------------------------------------------------------	
# permutes (derangements) some N1 random durations (in percents), some N2 random pitches (in percents) and some N3 random velocities of the midi sequence. permutations occur dependently from each other.
# INPUT: midi file name or midi list, N1, N2, N3, [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. 
# if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: midi list, midi file
# -----------------------------------------------------------------------------
def _midiPermutDPVIndep(midiInput, N1, N2, N3, writeMid = 'y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
		inFile = 'noFile'
	else:
		midiList = _midi2list(midiInput)
		inFile = os.path.basename(midiInput)
	
	nOfNotes = len(midiList)
	output = []

	# number of notes for independently changes of three parameters
	nToChange1 = round(nOfNotes*N1/100)
	nToChange2 = round(nOfNotes*N2/100)
	nToChange3 = round(nOfNotes*N3/100)
	
	# list with all indexes
	allInd = list(range(0, nOfNotes))
	
	# choosing some nToChange1 indexes for duration permutations
	dInd = random.sample(allInd, nToChange1)
	dInd.sort()
	# making a list with indexes with deranged duration indexes: making a derangement od allInd according to the map - dind
	(dIndDer, curSum, maxSum) = _partDerangeMap(allInd, dInd)
	
	# making a list with indexes with deranged velocities indexes: making a derangement od allInd according to the map - vInd. with while loop checking, if some note of the deranged output version the same parameters as the input midi list
	while True:
		equal = False
		pInd = random.sample(allInd, nToChange2)
		pInd.sort()
	# for velocities
		(pIndDer, curSum, maxSum) = _partDerangeMap(allInd, pInd)
		vInd = random.sample(allInd, nToChange3)
		vInd.sort()
	# for velocities
		(vIndDer, curSum, maxSum) = _partDerangeMap(allInd, vInd)
	
		#dp:
		for i in dInd:
			if dIndDer[i] == pIndDer[i]:
				equal = True
		
		for i in pInd:
			if dIndDer[i] == pIndDer[i]:
				equal = True
		
		#dv:
		for i in dInd:
			if dIndDer[i] == vIndDer[i]:
				equal = True
		
		for i in vInd:
			if dIndDer[i] == vIndDer[i]:
				equal = True
		#pv:
		for i in pInd:
			if pIndDer[i] == vIndDer[i]:
				equal = True
		
		for i in vInd:
			if pIndDer[i] == vIndDer[i]:
				equal = True
		
		
		if equal == False:
			break
	# building the output sequence
	for i in allInd:
		new = [midiList[i][0]]+[midiList[dIndDer[i]][1]]+[midiList[pIndDer[i]][2]]+[midiList[vIndDer[i]][3]]
		output.append(new)
	
	outFileN = 'indepPermDPV_'+inFile+'_'+ str(N1)+'-'+str(N2)+'-'+str(N3)
	if writeMid == 'y':
		_list2midi(output, outFileN+'.mid')
	
	if writeTxt=='y':
		# save as merged input as text file
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(output))
		outTextFile.close
	
	return output


# -----------------------------------------------------------------------------	
# permutes (derangements) durations of the elements with indexes from map1, pitches of the elements with indexes from map2 and velocities of the elements with indexes from map2 of the midi sequence. permutations occur independently from each other.
# INPUT: midi file name or midi list, map1, map2, map3, [writeTxt] and [writeMid] - write or no the list into a txt file and as midi file. both defaults: 'y'. 
# if midiInput is a list: CREATES THE NEW LIST
# OUTPUT: midi list, midi file
# -----------------------------------------------------------------------------
def _midiPermutDPVIndepMap(midiInput, map1, map2, map3, writeMid = 'y', writeTxt = 'y'):
	if type(midiInput).__name__ == 'list':
		midiList = copy.deepcopy(midiInput)
		inFile = 'noFile'
	else:
		midiList = _midi2list(midiInput)
		inFile = os.path.basename(midiFileName)
	
	nOfNotes = len(midiList)
	output = []

	
	# list with all indexes
	allInd = list(range(0, nOfNotes))
	
	# choosing some nToChange1 indexes for duration permutations
	dInd = map1
	dInd.sort()
	# making a list with indexes with deranged duration indexes: making a derangement od allInd according to the map - dind
	(dIndDer, curSum, maxSum) = _partDerangeMap(allInd, dInd)
	
	# making a list with indexes with deranged velocities indexes: making a derangement od allInd according to the map - vInd. with while loop checking, if some note of the deranged output version the same parameters as the input midi list
	while True:
		equal = False
		pInd = map2
		pInd.sort()
	# for velocities
		(pIndDer, curSum, maxSum) = _partDerangeMap(allInd, pInd)
		vInd = map3
		vInd.sort()
	# for velocities
		(vIndDer, curSum, maxSum) = _partDerangeMap(allInd, vInd)
	
		#dp:
		for i in dInd:
			if dIndDer[i] == pIndDer[i]:
				equal = True
		
		for i in pInd:
			if dIndDer[i] == pIndDer[i]:
				equal = True
		
		#dv:
		for i in dInd:
			if dIndDer[i] == vIndDer[i]:
				equal = True
		
		for i in vInd:
			if dIndDer[i] == vIndDer[i]:
				equal = True
		#pv:
		for i in pInd:
			if pIndDer[i] == vIndDer[i]:
				equal = True
		
		for i in vInd:
			if pIndDer[i] == vIndDer[i]:
				equal = True
		
		
		if equal == False:
			break
	# building the output sequence
	for i in allInd:
		new = [midiList[i][0]]+[midiList[dIndDer[i]][1]]+[midiList[pIndDer[i]][2]]+[midiList[vIndDer[i]][3]]
		output.append(new)
	
	outFileN = 'indepPermDPVMap_'+inFile
	if writeMid == 'y':
		_list2midi(output, outFileN+'.mid')
	
	if writeTxt=='y':
		# save as merged input as text file
		outTextFile = open(outFileN+'.txt', 'w')
		outTextFile.write(str(output))
		outTextFile.close
	
	return output

# -----------------------------------------------------------------------------
# microtonal rounding (1/8-tones), for a sequence as a list
# INPUT: midi list, [writeTxt] - write or no the list into a txt file default: 'y'. 
# CREATES THE NEW LIST
# OUTPUT: rounded midi list
# -----------------------------------------------------------------------------
def _midiMicroRound(midiList, writeTxt = 'y'):
	output = copy.deepcopy(midiList)
	microList = [0, 0.25, 0.5, 0.75, 1] # 
	for i in range(len(midiList)):
		difList = []
		for j in microList:
			difList.append(abs(midiList[i][2]-(int(midiList[i][2])+j)))
		print(difList)
		#finding min in difList
		_min = min(difList)
		for j in range(len(difList)):
			if difList[j] == _min:
				minInd = j
		output[i][2] = int(output[i][2])+microList[minInd]
	if writeTxt=='y':
		# save as merged input as text file
		outTextFileN = '_midiMicroRound.txt'
		outTextFile = open(outTextFileN, 'w')
		outTextFile.write(str(output))
		outTextFile.close
	return output




# takes a midi list and transforms absolute attack time to relative attack times
def _midiAbsToRel(midiInput):
	# each midi list consists of such element: [a, d, p, v]
	# output must be a list, whose each element is [nextAttack, d, p, v]
	# nextAttack - the time when the next attack comes
	midiInput = _sortLOL(midiInput, 0)
	output = []
	# iterating through the input list
	for i in range(len(midiInput)):
		if i != len(midiInput)-1:
			curOutEvent = [midiInput[i+1][0]-midiInput[i][0], midiInput[i][1], midiInput[i][2], midiInput[i][3]]
			output.append(curOutEvent)
		else:
			curOutEvent = [0, midiInput[i][1], midiInput[i][2], midiInput[i][3]]
			output.append(curOutEvent)
	return output

			


		
	



		
			
		
	
	



