diff --git a/FoxDot/lib/.version b/FoxDot/lib/.version index 287f2d52..f8067451 100644 --- a/FoxDot/lib/.version +++ b/FoxDot/lib/.version @@ -1 +1 @@ -0.8.12 \ No newline at end of file +0.8.13 \ No newline at end of file diff --git a/FoxDot/lib/Patterns/Main.py b/FoxDot/lib/Patterns/Main.py index af39e995..ee16a8c3 100644 --- a/FoxDot/lib/Patterns/Main.py +++ b/FoxDot/lib/Patterns/Main.py @@ -12,6 +12,7 @@ import functools import inspect +from math import floor # Decorator functions for nested expansion of pattern functions and methods @@ -76,7 +77,7 @@ def ClassPatternMethod(f): setattr(metaPattern, f.__name__, classmethod(f)) return -# Begin Pattern Abstratct Base Class +# Begin Pattern Abstract Base Class class metaPattern(object): """ Abstract base class for Patterns """ @@ -627,6 +628,126 @@ def shufflets(self, n): new = self.data[:] return Pattern([Pattern(new).shuffle().asGroup() for i in range(n)]) + + ''' + bubble_sort written by Dillon Dawson + https://github.com/Dillon-Dawson/MelodyExtFD + Part of his A Level Computer Science Project + ''' + def bubble_sort(self, swaps=False, debug = False, palindrome = False): + ''' Returns the pattern and each stage of the pattern going through a bubble sort + e.g. `P[50,25,5,20,10].bubble_sort()` will return `P[50,25,5,20,10,25,5,20,10,50,5,20,10,25,50,5,10,20,25,50]` + and `P[50,25,5,20,10].bubble_sort(swaps = True)` will add to the output for every number swap returning `P[50,25,5,20,10,25,50,5,20,10,25,5,50,20,10,25,5,20,50,10,25,5,20,10,50,5,25,20,10,50,5,20,25,10,50,5,20,10,25,50,5,10,20,25,50]` + and `P[0.5 ,2.0 ,1 ,1.5].bubble_sort(palindrome=True)` will also undo the change to smoothly return to the orignal pattern P[0.5, 2.0, 1, 1.5, 0.5, 1, 1.5, 2.0, 0.5, 1, 1.5, 2.0, 0.5, 2.0, 1, 1.5] + ''' + + items = self.data + changes = passes = 0 # This sets the changes and passes to 0 every time this section is ran. + last = len(items) #lens is a method that returns the length of a list. + swapped = True + output = [] + output += items #output = output + items + outputP = [] + outputP = items + outputP + + + while swapped: + swapped = False + passes += 1 + for j in range(1, last): + if items[j - 1] > items[j]: + items[j], items[j - 1] = items[j - 1], items[j] # Swap + if swaps == True: + output += items + changes += 1 + swapped = True + last = j + if changes == 0: + #raise Warning("Data Presorted") + print("Data Presorted", self.data) + if swapped == True and swaps == False: + output += items + outputP = items + outputP + + if palindrome: + output += outputP + if debug == True: + print(output) + return self.new(output) + #return self.__class__(output) + + + ''' + Shell_Sort and original tests written by Dillon Dawson + https://github.com/Dillon-Dawson/MelodyExtFD + Part of his A Level Computer Science Project + ''' + + def makehalves(self, length): + i = length + list = [] + while i>2: + i = floor(i/2) + if(i>1): + list.append(i) + return list + + def inssort2(self, items, start, incr): + output = [] + outputP = [] + + for i in range(start+incr, len(items), incr): + j=i + while (j>=incr) and (items[j] < items[j-incr]): + items[j], items[j-incr] = items[j-incr], items[j] + #print("Swapping", items) + output += items + outputP = items + outputP + j-=incr + + return(output,outputP) + + def shell_sort(self, swaps=False,debug = False, palindrome = False): #This defines the section of the code that does the + ''' Returns the pattern and each stage of the pattern going through a shell sort + e.g. `P[50,25,5,20,10].shell_sort()` will return `P[50, 25, 5, 20, 10, 5, 25, 10, 20, 50, 5, 20, 10, 25, 50, 5, 10, 20, 25, 50]` + and `P[50,25,5,20,10].shell_sort(swaps = True)` will add to the output for every number swap returning `P[50,25,5,20,10,5,25,50,20,10,5,25,10,20,50,5,20,10,25,50,5,10,20,25,50]` + and `P[50,25,5,20,10].shell_sort(palindrome=True)` will also undo the change to smoothly return to the orignal pattern P[50, 25, 5, 20, 10, 5, 25, 10, 20, 50, 5, 20, 10, 25, 50, 5, 10, 20, 25, 50,5, 10, 20, 25, 50,5, 20, 10, 25, 50,5, 25, 10, 20, 50,50, 25, 5, 20, 10] + ''' + items = self.data + output = [] + output += items #output = output + items + outputP = [] + outputP = items + outputP + + for i in self.makehalves(len(items)): + for j in range(i): + sortoutput, sortoutputP = self.inssort2(items, j, i) + #print("after inssort2(items, "+str(j)+", "+str(i)+")", items) + if swaps == False: + output += items + outputP = items + outputP + if swaps == True: + output += sortoutput + outputP = sortoutputP + outputP + sortoutput, sortoutputP = self.inssort2(items, 0, 1) + #print("After pass at 1", items) + if swaps == False: + output += items + outputP = items + outputP + if swaps == True: + output += sortoutput + outputP = sortoutputP + outputP + + if palindrome: + output += outputP + + if debug == True: + print("Output",output) + + return self.new(output) + + + # Loop methods @loop_pattern_method @@ -897,7 +1018,7 @@ def startswith(self, prefix): def all(self, func=(lambda x: bool(x))): """ Returns true if all of the patterns contents satisfies func(x) - default is nonzero """ - if len(self.data) is 0: + if len(self.data) == 0: return False for item in self.data: diff --git a/FoxDot/lib/Patterns/Sequences.py b/FoxDot/lib/Patterns/Sequences.py index 39d606df..1d63b32a 100644 --- a/FoxDot/lib/Patterns/Sequences.py +++ b/FoxDot/lib/Patterns/Sequences.py @@ -315,6 +315,33 @@ def PStrum(n=4): """ Returns a pattern of durations similar to how you might strum a guitar """ return (Pattern([1,1/2]).stutter([1,n + 1])|Pattern([1.5,1/2]).stutter([1,n])|1) +@loop_pattern_func +def PBase(n, b=2, l=1): + ''' Returns the 'n' number in base 'b' split into digits. + e.g. `PBase(5)` will return `P[1,0,1]` + and `PBase(5,4)` will return `P[1,1]` + and `PBase(5,4,4)` will return `P[0,0,1,1]` + ''' + + number = (0+n) if n>=0 else (abs(n)+b) + + from_base10_to_anybase_num = [] # Initialize the number in any base + while number > 0: # Iterate while the number is greater than zero + remainder = int(number % b) # change remainder in integer + #from_base10_to_anybase_num.append( remainder ) + from_base10_to_anybase_num.insert(0, remainder ) + number //= b # take the integer part after division + + number_list = [int(i) for i in from_base10_to_anybase_num] + + while(len(number_list) < l): + number_list.insert(0, 0) + + #print("from_10_to_anybase("+str(num)+","+str(base)+")", number_list) + + return Pattern( number_list ) + + def PQuicken(dur=1/2, stepsize=3, steps=6): """ Returns a PGroup of delay amounts that gradually decrease """ delay = [] diff --git a/FoxDot/lib/Players.py b/FoxDot/lib/Players.py index f701e4ef..817c9048 100644 --- a/FoxDot/lib/Players.py +++ b/FoxDot/lib/Players.py @@ -2142,6 +2142,25 @@ def __call__(self, *args, **kwargs): if callable(p): p.__call__(*args, **kwargs) + + +def group_method(f): + """ Decorator for assigning functions as Group methods. + If the function name contains "_group" that will be removed while assigning + allowing you to a have a function, a player method and group method all called the same thing + + >>> @group_method + ... def test(self): + ... print(self) + + >>> p1.test() + """ + name = f.__name__.replace("_group", "") + setattr(Group, name, f) + return getattr(Group, name) + +GroupMethod = group_method # Temporary alias + class rest(object): ''' Represents a rest when used with a Player's `dur` keyword ''' diff --git a/FoxDot/lib/__init__.py b/FoxDot/lib/__init__.py index 4974722e..2797b87d 100644 --- a/FoxDot/lib/__init__.py +++ b/FoxDot/lib/__init__.py @@ -48,6 +48,8 @@ def __getitem__(self, key): def player_method(f): """ Decorator for assigning functions as Player methods. + If the function name contains "_player" that will be removed while assigning + allowing you to a have a function a player method and group method all called the same thing >>> @player_method ... def test(self): @@ -55,8 +57,9 @@ def player_method(f): >>> p1.test() """ - setattr(Player, f.__name__, f) - return getattr(Player, f.__name__) + name = f.__name__.replace("_player", "") + setattr(Player, name, f) + return getattr(Player, name) PlayerMethod = player_method # Temporary alias @@ -88,7 +91,7 @@ def next_bar(n=0): nextBar = next_bar # temporary alias -def futureBar(n=0): +def future_bar(n=0): ''' Schedule functions when you define them with @futureBar Functions will run n bars in the future (0 is the next bar) @@ -100,6 +103,63 @@ def futureBar(n=0): ''' return _futureBarDecorator(n, Clock.bar_length()) +futureBar = future_bar # temporary alias + +@player_method +def soloBars(self,n=2,end=False): + ''' Solo's the current player from the next bar for the specified amount of bars + ''' + nextBar(self.solo) + soloEnd = Clock.next_bar() + (n * Clock.bar_length()) + Clock.schedule(self.metro.solo.reset, soloEnd) + if(end): + Clock.schedule(self.stop, soloEnd) + + +@player_method +def soloBeats(self, n=8, end=False): + ''' Solo's the current player from now for the specified amount of beats + ''' + Clock.schedule(self.solo, Clock.now()) + soloEnd = Clock.now() + n + Clock.schedule(self.metro.solo.reset, soloEnd) + if(end): + Clock.schedule(self.stop, soloEnd) + + +@group_method +def soloBars_group(self,n=2, end=False): + ''' Solo's the current group from the next bar for the specified amount of bars + ''' + if self.metro is None: + self.__class__.metro = Player.metro + + soloEnd = Clock.next_bar() + (n * Clock.bar_length()) + Clock.schedule(self.metro.solo.reset, soloEnd) + if(end): + for player in list(self.metro.playing): + if player in self.players: + Clock.schedule(player.stop, soloEnd) + + nextBar(self.solo) + +@group_method +def soloBeats_group(self,n=8, end=False): + ''' Solo's the current group from now for the specified amount of beats + ''' + if self.metro is None: + self.__class__.metro = Player.metro + + soloEnd = Clock.now() + n + Clock.schedule(self.metro.solo.reset, soloEnd) + if(end): + for player in list(self.metro.playing): + if player in self.players: + Clock.schedule(player.stop, soloEnd) + + Clock.schedule(self.solo, Clock.now()) + + def update_foxdot_clock(clock): """ Tells the TimeVar, Player, and MidiIn classes to use a new instance of TempoClock. """ diff --git a/default.nix b/default.nix new file mode 100644 index 00000000..9438ed88 --- /dev/null +++ b/default.nix @@ -0,0 +1,18 @@ +{ lib, pkgs ? import { } }: + +with pkgs.python310Packages; + +buildPythonPackage rec { + name = "FoxDot"; + version = "0.1"; + src = ./.; + propagatedBuildInputs = [ setuptools wheel tkinter ]; + pythonImportsCheck = [ "FoxDot" ]; + doCheck = false; + + meta = with lib; { + description = "Our FoxDot clone"; + homepage = "https://github.com/UTCSheffield/FoxDot"; + maintainers = with maintainers; [ devramsean0 ]; + }; +} \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..679fd7dd --- /dev/null +++ b/shell.nix @@ -0,0 +1,8 @@ +{ pkgs ? import {} }: + +with pkgs; + +mkShell { + name = "FoxDot"; + buildInputs = [ (callPackage ./. {}) ]; +} \ No newline at end of file