diff options
Diffstat (limited to 'filemix.py')
-rwxr-xr-x | filemix.py | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/filemix.py b/filemix.py new file mode 100755 index 0000000..a4e683f --- /dev/null +++ b/filemix.py @@ -0,0 +1,334 @@ +# FILEMIX - Audio File Player/Mixer/Processor Utilities for Children (2010) +# Art Hunkins (www.arthunkins.com) +# +# FileMix is licensed under the Creative Commons Attribution-Share +# Alike 3.0 Unported License. To view a copy of this license, visit +# http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to +# Creative Commons, 171 Second Street, Suite 300, San Francisco, +# California, 94105, USA. +# +# It is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# version 1 +# +# Notes: +# +# The four versions that comprise the FileMix utility series are: +# FileMix4, FileMix4ASC, FileMix and FileMixASC. FileMix4 and +# FileMix4ASC require 4 stereo soundfiles, while FileMix and +# FileMixASC can handle 1-4 mono or stereo soundfiles. The files +# can be of any sample rate and a variety of uncompressed formats +# including WAV and AIFF; also Ogg/Vorbis, but not MP3. The Ogg/ +# Vorbis format is only possible when the Sugar version is later +# than 0.84, which excludes the XO-1 and SoaS Strawberry. The XO-1.5 +# works fine with Ogg. +# +# Actually, the ogg vorbis format (which is written by the Record +# activity) can be used by SoaS Strawberry (0.84) if libsndfile is +# updated. This can be done while connected to the internet by +# issuing the following commands in the Terminal: +# su <Enter> +# yum update libsndfile <Enter> +# +# Students are encouraged to create their own soundfiles, especially +# to make their own nature soundscapes. (This is the primary intent +# behind these utilities. The four short "nature" files included here +# are abbreviated versions of those from the author's DUSK AT ST. +# FRANCIS SPRINGS [www.arthunkins.com].) +# +# The natural vehicle for soundfile creation is the Record activity. +# This activity is fairly simple and straightforward; the only problem +# is that certain versions of it work poorly or not at all. The +# following versions of Record seem to work well or fairly well: v59 +# (well) with the XO-1; v67 or v85/86 (fairly well) with XO-1.5; v64 +# (well) with SoaS Strawberry; v66 (fairly well) with SoaS Blueberry, +# Mirabelle and later. (This is as of 9/2010.) +# +# Soundfiles must be moved into the folder where this file resides, +# and be renamed soundin.1 through soundin.4. +# +# Important: The controller must be attached AFTER boot, and BEFORE +# the version is selected. It is assumed that the controller is a USB +# device. The inexpensive Korg nanoKontrol is one appropriate +# controller choice; it can nicely handle either 8- or 9-slider +# renditions (but not those requiring more than 9 sliders). Choose +# Scene 4 on the Korg, and Channel "0" in the performance window. +# +# It is also important, with all versions, to wait 3 to 5 seconds +# after pressing START, before moving any controls or pressing more +# buttons/keys. The utilities will not respond to these actions until +# after this time. +# +# To facilitate performance of FileMix on the Korg nanoKontrol, its STOP +# button has been programmed as a Delay Start option (buttons on the +# device, by default, are not set to MIDI notes). It is conceivable +# that this feature could interfere with MIDI controller selection +# (Continuous Controller numbers). Solutions to this possible issue +# are discussed in the FileMix.txt document on the author's website. +# Other relevent items of interest may also be found in this document. +# +# On a few systems, e.g. the Intel Classmate PC, the specified sr +# (sample rate) of 44100 may not produce audio. Substitute a rate of +# 48000 toward the beginning of each .csd file, using a text editor. +# +# If you get audio glitching, open Sugar's Control Panel, and turn off +# Extreme power management (under Power) or Wireless radio (under +# Network). Stereo headphones (an inexpensive set will work fine) or +# external amplifier/speaker system are highly recommended. +# +# The font display of this activity can be resized in csndsugui.py, +# using any text editor. Further instructions are found toward the +# beginning of csndsugui.py. + +import csndsugui +from sugar.activity import activity +import gtk +import os + +class FileMix(activity.Activity): + + def __init__(self, handle): + + activity.Activity.__init__(self, handle) + + red = (0xDDDD, 0, 0) + brown = (0x6600, 0, 0) + green = (0, 0x5500, 0) + + win = csndsugui.CsoundGUI(self) + width = gtk.gdk.screen_width() + height = gtk.gdk.screen_height() + if os.path.exists("/etc/olpc-release") or os.path.exists("/sys/power/olpc-pm"): + tall = 1 + adjust = 78 + else: + adjust = 57 + screen = win.box() + screen.set_size_request(width, height - adjust) + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + screen.pack_start(scrolled) + all = gtk.VBox() + all.show() + scrolled.add_with_viewport(all) + scrolled.show() + + win.text("<big><b><big><u>FILEMIX</u> - Audio File Player/Mixer/Processor \ +Utilities for Children (2010)</big></b>\n\ +\t\t\t\t Art Hunkins (www.arthunkins.com)</big>", all) + + win.text("\ +<b>FileMix4</b> and <b>FileMix4ASC</b> play 4 stereo files; any format except mp3 \ +(and no ogg on XO-1 and Sugar 0.84/Strawberry).\n\ +<b>FileMix</b> and <b>FileMixASC</b> play 1-4 mono and/or stereo files; \ +any format as above.\n\ + Files must be named <i>soundin.1</i> to <i>soundin.4</i> \ +and be located in this folder.\n\ + The default files are abbreviated versions of those from the author's \ +<b>DUSK AT ST. FRANCIS SPRINGS</b>.\n\ + You are urged to make your own, perhaps with the Record activity \ +(in ogg format) - especially nature soundscapes.", all, brown) + win.text("<b>FileMix4</b> and <b>FileMix</b> require MIDI controllers; \ +the # of knobs/sliders depends on the # of files/parameters you control. \n\ + All knobs/sliders are adjoining, and like parameters are grouped: \ +volume, then peak freq, peak gain, lastly speed/freq.\n\ + A Master volume control (all files are affected) is optional, and may \ +be separated from the other controls.", all, green) + win.text("<b>FileMix4ASC</b> and <b>FileMIXASC</b> don't use MIDI; \ +control is via 1 or more ASCII keyboards.\n\ + <b>ASCII keys</b> used: (glide time in seconds) 1-0(10); ` (to left of 1) \ += 0(.1) seconds (takes effect with next keypress) - \n\ + (volume) A,S,D,F - (band-pass freq peak) Q,W,E,R - (peak gain) J,K,L,; - \ +(speed/freq) U,I,O,P\n\ + Unshifted=up by .5 (1 max); shifted=down by .5 (0 min); \ +<b>top alphabetic rows go <i>negative as well as positive</i></b>.\n\ + G,H = all files off; V,B,N = all files off and reset to \ +defaults. Note: SPACE bar and ENTER only START and STOP.", all, brown) + win.text("<b>FileMix(ASC)</b> offers delayed start - for single play, sync/\ +'play from head' options. Hit ASCII key/MIDI note to begin play. ", all, +green) + + win.text("\ +\t<b>1 - FileMix4</b> Simple; MIDI controller with 4-16(17) knobs/sliders \ +(#17 = optional Master volume)\n\ +\t<b>2 - FileMix4ASC</b> Simple; 1 or more ASCII keyboards\n\ +\t<b>3 - FileMix</b> \ +Advanced; MIDI controller with 1-16(17) knobs/sliders (#17 = optional \ +Master volume)\n\ +\t<b>4 - FileMixASC</b> Advanced; 1 or more ASCII keyboards\n\ +<i><b>MIDI</b>: plug in controller after boot & before selecting. \ +Zero controls before start; reset peak freq & speed/freqs \ +to .5 after.\n<b>ALL VERSIONS</b>: IMPORTANT! \ +Key presses & controller motion prior to 3-5 seconds after hitting START \ +are ignored.</i> ", all, brown) + nbox = win.box(False, all) + win.text("", nbox) + but1 = win.cbbutton(nbox, self.version1, " 1 FileMix4 ") + but1.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + but1.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + but2 = win.cbbutton(nbox, self.version2, "2 FileMix4ASC") + but2.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + but2.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + but3 = win.cbbutton(nbox, self.version3, " 3 FileMix ") + but3.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + but3.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + but4 = win.cbbutton(nbox, self.version4, " 4 FileMixASC ") + but4.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + but4.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + win.text(" <b>MIDI DEVICE REQUIRED</b> for 1 & 3", nbox, brown) + bbox = win.box(False, all) + self.bb = bbox + self.w = win + self.r = red + self.g = green + self.br = brown + self.ver = 0 + + def onKeyPress(self, widget, event): + if self.p: + if self.ver > 1: + self.w.set_channel("ascii", event.keyval) + + def playcsd(self, widget): + if self.p == False: + self.p = True + self.w.play() + self.but.child.set_label("STOP !") + self.but.child.set_use_markup(True) + self.but.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0xFFFF, 0, 0)) + self.but.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0xFFFF, 0, 0)) + if self.ver > 1: + self.connect("key-press-event", self.onKeyPress) + else: + self.p = False + self.w.recompile() + self.w.channels_reinit() + self.but.child.set_label("START !") + self.but.child.set_use_markup(True) + self.but.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + self.but.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + + def version1(self, widget): + if self.ver != 0: + self.box1.destroy() + self.box2.destroy() + self.ver = 1 + self.box1 = self.w.box(True, self.bb) + self.w.text("", self.box1) + self.box2 = self.w.box(True, self.bb) + self.f = self.w.framebox(" <b>1 - FileMix4</b> ", False, self.box2, self.r) + self.b1 = self.w.box(True, self.f) + self.b2 = self.w.box(True, self.f) + self.b3 = self.w.box(True, self.f) + self.b4 = self.w.box(True, self.f) + self.w.reset() + self.w.csd("FileMix4.csd") + self.w.spin(0, 0, 16, 1, 1, self.b1, 0, "chan", "Channel # [0=CC7,\n \ +channels 1-16]") + self.w.spin(20, 0, 120, 1, 1, self.b1, 0, "ctrl1", "1st Control Bank1") + self.w.spin(28, 0, 120, 1, 1, self.b2, 0, "ctrl2", "1st Control Bank2") + self.w.spin(9, -1, 127, 1, 1, self.b2, 0, "mast", "Master Controller\n\ + [-1=no Master]") + self.w.spin(4, 1, 4, 1, 1, self.b3, 0, "parms", " # of Parameters\n\ + [1=amp 2=band-\n pass peak freq\n 3=peak gain\n 4=speed/freq]") + self.p = False + self.w.text("\n<i>Select options first </i>", self.b4, self.g) + self.but = self.w.cbbutton(self.b4, self.playcsd, "START !") + self.but.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + self.but.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + + def version2(self, widget): + if self.ver != 0: + self.box1.destroy() + self.box2.destroy() + self.ver = 2 + self.box1 = self.w.box(True, self.bb) + self.w.text("\t\t\t ", self.box1) + self.box2 = self.w.box(True, self.bb) + self.f = self.w.framebox(" <b>2 - FileMix4ASC</b> ", False, self.box2, self.r) + self.b1 = self.w.box(True, self.f) + self.b2 = self.w.box(True, self.f) + self.w.reset() + self.w.csd("FileMix4ASC.csd") + self.w.spin(4, 1, 4, 1, 1, self.b1, 0, "parms", " # of Parameters\n\ + [1=amp 2=band-\n pass peak freq\n 3=peak gain\n 4=speed/freq]") + self.p = False + self.w.text("\n<i>Select option first </i>", self.b2, self.g) + self.but = self.w.cbbutton(self.b2, self.playcsd, "START !") + self.but.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + self.but.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + + def version3(self, widget): + if self.ver != 0: + self.box1.destroy() + self.box2.destroy() + self.ver = 3 + self.box1 = self.w.box(True, self.bb) + self.w.text(" ", self.box1) + self.box2 = self.w.box(True, self.bb) + self.f = self.w.framebox(" <b>3 - FileMix</b> ", False, self.box2, self.r) + self.b1 = self.w.box(True, self.f) + self.b2 = self.w.box(True, self.f) + self.b3 = self.w.box(True, self.f) + self.b4 = self.w.box(True, self.f) + self.b5 = self.w.box(True, self.f) + self.b6 = self.w.box(True, self.f) + self.w.reset() + self.w.csd("FileMix.csd") + self.w.spin(0, 0, 16, 1, 1, self.b1, 0, "chan", "Channel # [0=CC7,\n \ +channels 1-16]") + self.w.spin(20, 0, 120, 1, 1, self.b1, 0, "ctrl1", "1st Control Bank1") + self.w.spin(28, 0, 120, 1, 1, self.b2, 0, "ctrl2", "1st Control Bank2") + self.w.spin(9, -1, 127, 1, 1, self.b2, 0, "mast", "Master Controller\n\ + [-1=no Master]") + self.w.spin(4, 1, 4, 1, 1, self.b3, 0, "parms", " # of Parameters\n\ + [1=amp 2=band-\n pass peak freq\n 3=peak gain\n 4=speed/freq]") + self.w.spin(4, 1, 4, 1, 1, self.b4, 0, "files", "# of Files") + self.w.button(self.b4, "random", "Start at Head?") + self.w.button(self.b4, "loop", "Single Play ?") + self.w.button(self.b5, "delay", "Delay Start ?") + self.w.text("If delay, hit any key/\nMIDI note to begin.\n\ + [Volumes up first,\n & set fadein/out.]", self.b5, self.g) + self.w.spin(-2, -3, 30, 1, 1, self.b6, 0, "fade", " FadeIn/Out Seconds\n\ +[-3=.01/-2=.1/-1=.5]") + self.p = False + self.w.text("<i>Select options first </i>", self.b6, self.g) + self.but = self.w.cbbutton(self.b6, self.playcsd, "START !") + self.but.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + self.but.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + + def version4(self, widget): + if self.ver != 0: + self.box1.destroy() + self.box2.destroy() + self.ver = 4 + self.box1 = self.w.box(True, self.bb) + self.w.text("\t\t\t\t\t\t\t\t ", self.box1) + self.box2 = self.w.box(True, self.bb) + self.f = self.w.framebox(" <b>4 - FileMixASC</b> ", False, self.box2, \ +self.r) + self.b1 = self.w.box(True, self.f) + self.b2 = self.w.box(True, self.f) + self.b3 = self.w.box(True, self.f) + self.b4 = self.w.box(True, self.f) + self.w.reset() + self.w.csd("FileMixASC.csd") + self.w.spin(4, 1, 4, 1, 1, self.b1, 0, "parms", " # of Parameters\n\ + [1=amp 2=band-\n pass peak freq\n 3=peak gain\n 4=speed/freq]") + self.w.spin(4, 1, 4, 1, 1, self.b2, 0, "files", "# of Files") + self.w.button(self.b2, "random", "Start at Head?") + self.w.button(self.b2, "loop", "Single Play ?") + self.w.button(self.b3, "delay", "Delay Start ?") + self.w.text(" If delay, press T\n or Y to begin.\n\ +[Volumes up first,\n& set fadein/out.]", self.b3, self.g) + self.w.spin(-2, -3, 30, 1, 1, self.b4, 0, "fade", " FadeIn/Out Seconds\n\ +[-3=.01/-2=.1/-1=.5]") + self.p = False + self.w.text("<i>Select options first </i>", self.b4, self.g) + self.but = self.w.cbbutton(self.b4, self.playcsd, "START !") + self.but.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0x7700, 0)) + self.but.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(0, 0x7700, 0)) + + |