From 5140276e1926e5cbbbbb8ffdc2dd923c5a7a94dc Mon Sep 17 00:00:00 2001 From: Paul Brossier Date: Wed, 15 Feb 2006 11:36:17 +0000 Subject: [PATCH] added aubioweb added aubioweb --- python/aubio/web/__init__.py | 0 python/aubio/web/browser.py | 167 ++++++++++++++++++++ python/aubio/web/html.py | 287 +++++++++++++++++++++++++++++++++++ python/aubioweb.py | 113 ++++++++++++++ 4 files changed, 567 insertions(+) create mode 100644 python/aubio/web/__init__.py create mode 100644 python/aubio/web/browser.py create mode 100644 python/aubio/web/html.py create mode 100644 python/aubioweb.py diff --git a/python/aubio/web/__init__.py b/python/aubio/web/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/aubio/web/browser.py b/python/aubio/web/browser.py new file mode 100644 index 00000000..6adf8f63 --- /dev/null +++ b/python/aubio/web/browser.py @@ -0,0 +1,167 @@ + # + # Copyright 2004 Apache Software Foundation + # + # Licensed under the Apache License, Version 2.0 (the "License"); you + # may not use this file except in compliance with the License. You + # may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + # implied. See the License for the specific language governing + # permissions and limitations under the License. + # + # Originally developed by Gregory Trubetskoy. + # + # $Id: publisher.py,v 1.36 2004/02/16 19:47:27 grisha Exp $ + +""" + This handler is conceputally similar to Zope's ZPublisher, except + that it: + + 1. Is written specifically for mod_python and is therefore much faster + 2. Does not require objects to have a documentation string + 3. Passes all arguments as simply string + 4. Does not try to match Python errors to HTTP errors + 5. Does not give special meaning to '.' and '..'. + + This is a modified version of mod_python.publisher.handler Only the first + directory argument is matched, the rest is left for path_info. A default + one must be provided. + +""" + +from mod_python import apache +from mod_python import util +from mod_python.publisher import resolve_object,process_auth,imp_suffixes + +import sys +import os +import re + +from types import * + +def configure_handler(req,default): + + req.allow_methods(["GET", "POST"]) + if req.method not in ["GET", "POST"]: + raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED + + func_path = "" + if req.path_info: + func_path = req.path_info[1:] # skip first / + #func_path = func_path.replace("/", ".") + #if func_path[-1:] == ".": + # func_path = func_path[:-1] + # changed: only keep the first directory + func_path = re.sub('/.*','',func_path) + + # default to 'index' if no path_info was given + if not func_path: + func_path = "index" + + # if any part of the path begins with "_", abort + if func_path[0] == '_' or func_path.count("._"): + raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND + + ## import the script + path, module_name = os.path.split(req.filename) + if not module_name: + module_name = "index" + + # get rid of the suffix + # explanation: Suffixes that will get stripped off + # are those that were specified as an argument to the + # AddHandler directive. Everything else will be considered + # a package.module rather than module.suffix + exts = req.get_addhandler_exts() + if not exts: + # this is SetHandler, make an exception for Python suffixes + exts = imp_suffixes + if req.extension: # this exists if we're running in a | .ext handler + exts += req.extension[1:] + if exts: + suffixes = exts.strip().split() + exp = "\\." + "$|\\.".join(suffixes) + suff_matcher = re.compile(exp) # python caches these, so its fast + module_name = suff_matcher.sub("", module_name) + + # import module (or reload if needed) + # the [path] argument tells import_module not to allow modules whose + # full path is not in [path] or below. + config = req.get_config() + autoreload=int(config.get("PythonAutoReload", 1)) + log=int(config.get("PythonDebug", 0)) + try: + module = apache.import_module(module_name, + autoreload=autoreload, + log=log, + path=[path]) + except ImportError: + et, ev, etb = sys.exc_info() + # try again, using default module, perhaps this is a + # /directory/function (as opposed to /directory/module/function) + func_path = module_name + module_name = "index" + try: + module = apache.import_module(module_name, + autoreload=autoreload, + log=log, + path=[path]) + except ImportError: + # raise the original exception + raise et, ev, etb + + # does it have an __auth__? + realm, user, passwd = process_auth(req, module) + + # resolve the object ('traverse') + try: + object = resolve_object(req, module, func_path, realm, user, passwd) + except AttributeError: + # changed, return the default path instead + #raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND + object = default + # not callable, a class or an unbound method + if (not callable(object) or + type(object) is ClassType or + (hasattr(object, 'im_self') and not object.im_self)): + + result = str(object) + + else: + # callable, (but not a class or unbound method) + + # process input, if any + req.form = util.FieldStorage(req, keep_blank_values=1) + + result = util.apply_fs_data(object, req.form, req=req) + + if result or req.bytes_sent > 0 or req.next: + + if result is None: + result = "" + else: + result = str(result) + + # unless content_type was manually set, we will attempt + # to guess it + if not req._content_type_set: + # make an attempt to guess content-type + if result[:100].strip()[:6].lower() == '' \ + or result.find(' 0: + req.content_type = 'text/html' + else: + req.content_type = 'text/plain' + + if req.method != "HEAD": + req.write(result) + else: + req.write("") + return apache.OK + else: + req.log_error("mod_python.publisher: %s returned nothing." % `object`) + return apache.HTTP_INTERNAL_SERVER_ERROR + diff --git a/python/aubio/web/html.py b/python/aubio/web/html.py new file mode 100644 index 00000000..4b9fc5ff --- /dev/null +++ b/python/aubio/web/html.py @@ -0,0 +1,287 @@ +from aubio.bench.node import * + +def parse_args(req): + req.basehref = BASEHREF + req.datadir = DATADIR + if req.path_info: path_info = req.path_info + else: path_info = '/' + location = re.sub('^/show_[a-z0-9]*/','',path_info) + location = re.sub('^/play_[a-z0-9]*/','',location) + location = re.sub('^/index/','',location) + location = re.sub('^/','',location) + location = re.sub('/$','',location) + datapath = "%s/%s" % (DATADIR,location) + respath = "%s/%s" % (DATADIR,location) + last = re.sub('/$','',location) + last = last.split('/')[-1] + first = path_info.split('/')[1] + # store some of this in the mp_request + req.location, req.datapath, req.respath = location, datapath, respath + req.first, req.last = first, last + + if location: + if not (os.path.isfile(datapath) or + os.path.isdir(datapath) or + location in ['feedback','email']): + # the path was not understood + from mod_python import apache + req.write(" path not found %s" % (datapath)) + raise apache.SERVER_RETURN, apache.OK + #from mod_python import apache + #raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND + +def navigation(req): + """ main html navigation header """ + from mod_python import psp + req.content_type = "text/html" + parse_args(req) + datapath = req.datapath + location = req.location + + # deal with session + if req.sess.is_new(): + msg = "Welcome %s
" % req.sess['login'] + else: + msg = "Welcome back %s
" % req.sess['login'] + + # start writing + tmpl = psp.PSP(req, filename='header.tmpl') + tmpl.run(vars = { 'title': "aubioweb / %s / %s" % (req.first,location), + 'basehref': '/~piem/', + 'message': msg, + 'action': req.first}) + + req.write("

Content of ") + print_link(req,"","/") + y = location.split('/') + for i in range(len(y)-1): + print_link(req,"/".join(y[:i+1]),y[i]) + req.write(" / ") + req.write("%s

\n" % y[-1]) + + a = {'show_info' : 'info', + 'show_sound': 'waveform', + 'show_onset': 'onset', + 'index' : 'index', + 'show_pitch': 'pitch', + 'play_m3u': 'stream (m3u/ogg)', + 'play_ogg': 'save (ogg)', + 'play_wav': 'save (wav)', + } + + # print task lists (only remaining tasks) + print_link(req,re.sub('%s.*'%req.last,'',location),"go up") + akeys = a.keys(); akeys.sort(); + curkey = req.first + for akey in akeys: + if akey != curkey: + req.write(":: ") + print_link(req,"/".join((akey,location)),a[akey]) + else: + req.write(":: ") + req.write("%s" % a[akey]) + req.write("
") + + # list the content of the directories + listdir,listfiles = [],[] + if os.path.isdir(datapath): + listfiles = list_snd_files(datapath) + listdir = list_dirs(datapath) + listdir.pop(0) # kick the current dir + elif os.path.isfile(datapath): + listfiles = [datapath] + listdir = [re.sub(req.last,'',location)] + + link_list(req,listdir,title="Subdirectories") + link_list(req,listfiles,title="Files") + +def footer(req): + """ html navigation footer """ + from mod_python import psp + tmpl = psp.PSP(req, filename='footer.tmpl') + tmpl.run(vars = { 'time': -req.mtime+req.request_time }) + +def apply_on_data(req, func,**keywords): + # bug: hardcoded snd file filter + act_on_data(func,req.datapath,req.respath, + filter="f -maxdepth 1 -name '*.wav' -o -name '*.aif'",**keywords) + +def print_link(req,target,name,basehref=BASEHREF): + req.write("%s\n" % (basehref,target,name)) + +def print_img(req,target,name='',basehref=BASEHREF): + if name == '': name = target + req.write("%s\n" % (basehref,target,name,name)) + +def link_list(req,targetlist,basehref=BASEHREF,title=None): + if len(targetlist) > 1: + if title: req.write("

%s

"%title) + req.write('') + +def print_list(req,list): + req.write("
\n")
+    for i in list: req.write("%s\n" % i)
+    req.write("
\n") + +def print_command(req,command): + req.write("

%s

\n" % re.sub('%%','%',command)) + def print_runcommand(input,output): + cmd = re.sub('(%)?%i','%s' % input, command) + cmd = re.sub('(%)?%o','%s' % output, cmd) + print_list(req,runcommand(cmd)) + apply_on_data(req,print_runcommand) + +def datapath_to_location(input): + location = re.sub(DATADIR,'',input) + return re.sub('^/*','',location) + +## drawing hacks +def draw_func(req,func): + import re + req.content_type = "image/png" + # build location (strip the func_path, add DATADIR) + location = re.sub('^/draw_[a-z]*/','%s/'%DATADIR,req.path_info) + location = re.sub('.png$','',location) + if not os.path.isfile(location): + from mod_python import apache + raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND + # replace location in func + cmd = re.sub('(%)?%i','%s' % location, func) + # add PYTHONPATH at the beginning, + cmd = "%s%s 2> /dev/null" % (PYTHONPATH,cmd) + for each in runcommand(cmd): + req.write("%s\n"%each) + +def show_task(req,task): + def show_task_file(input,output,task): + location = datapath_to_location(input) + print_img(req,"draw_%s/%s" % (task,location)) + navigation(req) + req.write("

%s

\n" % task) + apply_on_data(req,show_task_file,task=task) + footer(req) + +## waveform_foo +def draw_sound(req): + #draw_func(req,"aubioplot-audio %%i stdout 2> /dev/null") + draw_func(req,"gdtest %%i 2> /dev/null") + +def show_sound(req): + show_task(req,"sound") + +## pitch foo +def draw_pitch(req,threshold='0.3'): + draw_func(req,"aubiopitch -i %%i -p -m yin -t %s -O stdout -B 2048 -H 1024" % threshold) + +def show_pitch(req): + show_task(req,"pitch") + +## onset foo +def draw_onset(req,threshold='0.3'): + draw_func(req,"aubiocut -i %%i -p -m complex -t %s -O stdout" % threshold) + +def show_onset(req,threshold='0.3',details=''): + def onset_file(input,output): + location = datapath_to_location(input) + print_img(req,"draw_onset/%s?threshold=%s"%(location,threshold)) + print_link(req,"?threshold=%s" % (float(threshold)-0.1),"-") + req.write("%s\n" % threshold) + print_link(req,"?threshold=%s" % (float(threshold)+0.1),"+") + # bug: hardcoded sndfile extension + anote = re.sub('\.wav$','.txt',input) + if anote == input: anote = "" + res = get_extract(input,threshold) + if os.path.isfile(anote): + tru = get_anote(anote) + print_list(req,get_results(tru,res,0.05)) + else: + req.write("no ground truth found
\n") + if details: + req.write("

Extraction

\n") + print_list(req,res) + else: + req.write("details
\n" % + (req.basehref,location,threshold)) + if details and os.path.isfile(anote): + req.write("

Computed differences

\n") + ldiffs = get_diffs(tru,res,0.05) + print_list(req,ldiffs) + req.write("

Annotations

\n") + print_list(req,tru) + navigation(req) + req.write("

Onset

\n") + apply_on_data(req,onset_file) + footer(req) + +def get_anote(anote): + import aubio.onsetcompare + # FIXME: should import with txtfile.read_datafile + return aubio.onsetcompare.load_onsets(anote) + +def get_diffs(anote,extract,tol): + import aubio.onsetcompare + return aubio.onsetcompare.onset_diffs(anote,extract,tol) + +def get_extract(datapath,threshold='0.3'): + cmd = "%saubiocut -v -m complex -t %s -i %s" % (PYTHONPATH,threshold,datapath) + lo = runcommand(cmd) + for i in range(len(lo)): lo[i] = float(lo[i]) + return lo + +def get_results(anote,extract,tol): + import aubio.onsetcompare + orig, missed, merged, expc, bad, doubled = aubio.onsetcompare.onset_roc(anote,extract,tol) + s =("GD %2.8f\t" % (100*float(orig-missed-merged)/(orig)), + "FP %2.8f\t" % (100*float(bad+doubled)/(orig)) , + "GD-merged %2.8f\t" % (100*float(orig-missed)/(orig)) , + "FP-pruned %2.8f\t" % (100*float(bad)/(orig)) ) + return s + +# play m3u foo +def play_m3u(req): + def show_task_file(input,output,task): + location = datapath_to_location(input) + req.write("http://%s%s/play_ogg/%s\n" % (HOSTNAME,BASEHREF,re.sub("play_m3u",task,location))) + req.content_type = "audio/mpegurl" + parse_args(req) + apply_on_data(req,show_task_file,task="play_ogg") + +# play wav foo +def play_wav(req): + req.content_type = "audio/x-wav" + func = "cat %%i" + # build location (strip the func_path, add DATADIR) + location = re.sub('^/play_wav/','%s/'%DATADIR,req.path_info) + if not os.path.isfile(location): + from mod_python import apache + raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND + # replace location in func + cmd = re.sub('(%)?%i','%s' % location, func) + # add PYTHONPATH at the beginning, + cmd = "%s 2> /dev/null" % cmd + for each in runcommand(cmd): + req.write("%s\n"%each) + +# play ogg foo +def play_ogg(req): + req.content_type = "application/ogg" + func = "oggenc -o - %%i" + # build location (strip the func_path, add DATADIR) + location = re.sub('^/play_ogg/','%s/'%DATADIR,req.path_info) + location = re.sub('.ogg$','',location) + if not os.path.isfile(location): + from mod_python import apache + raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND + # replace location in func + cmd = re.sub('(%)?%i','%s' % location, func) + # add PYTHONPATH at the beginning, + cmd = "%s 2> /dev/null" % cmd + for each in runcommand(cmd): + req.write("%s\n"%each) diff --git a/python/aubioweb.py b/python/aubioweb.py new file mode 100644 index 00000000..59c41c54 --- /dev/null +++ b/python/aubioweb.py @@ -0,0 +1,113 @@ +#!/usr/bin/python + +doc = """ +This script works with mod_python to browse a collection of annotated wav files +and results. + +you will need to have at least the following packages need to be installed (the +name of the command line tool is precised in parenthesis): + +libapache-mod-python (apache2 prefered) +sndfile-programs (sndfile-info) +vorbis-tools (oggenc) +python-gnuplot +python-numarray + +Try the command line tools in aubio/python to test your installation. + +NOTE: this script is probably horribly insecure. + +example configuration for apache to put in your preferred virtual host. + + + # Minimal config + AddHandler mod_python .py + # Disable these in production + PythonDebug On + PythonAutoReload on + # Default handler in url + PythonHandler aubioweb + ## Authentication stuff (optional) + #PythonAuthenHandler aubioweb + #AuthType Basic + #AuthName "Restricted Area" + #require valid-user + # make default listing + DirectoryIndex aubioweb/ + + +""" + +from aubio.web.html import * + +def handler(req): + from aubio.web.browser import * + from mod_python import Session + req.sess = Session.Session(req) + req.sess['login']='new aubio user' + req.sess.save() + return configure_handler(req,index) + +def index(req,threshold='0.3'): + navigation(req) + print_command(req,"sfinfo %%i") + return footer(req) + +def show_info(req,verbose=''): + navigation(req) + print_command(req,"sndfile-info %%i") + return footer(req) + +def feedback(req): + navigation(req) + req.write(""" + Please provide feedback below: +

+

+ Name:
+ Email:
+ Comment:
+ +
+ """) + +WEBMASTER='piem@calabaza' +SMTP_SERVER='localhost' + +def email(req,name,email,comment): + import smtplib + # make sure the user provided all the parameters + if not (name and email and comment): + return "A required parameter is missing, \ + please go back and correct the error" + # create the message text + msg = """\ +From: %s +Subject: feedback +To: %s + +I have the following comment: + +%s + +Thank You, + +%s + +""" % (email, WEBMASTER, comment, name) + # send it out + conn = smtplib.SMTP(SMTP_SERVER) + try: + conn.sendmail(email, [WEBMASTER], msg) + except smtplib.SMTPSenderRefused: + return """please provide a valid email""" + + conn.quit() + # provide feedback to the user + s = """\ + +Dear %s,
+Thank You for your kind comments, we +will get back to you shortly. +""" % name + return s -- 2.26.2