Hi,

Because I want to use my FR as my daily phone
and because I have no solution to perform the hardware buzz-fix yet,
I decided to write an application which allows to quickly and easily
change essential mixer settings.

According to
http://wiki.openmoko.org/wiki/Neo_FreeRunner_Hardware_Issues#Active_Issues
and
http://www.mail-archive.com/[email protected]/msg00564.html
3 mixers settings (5, 12, 48) help to reduce buzz intensity and
and 2 others (4, 6) affect the volume of the caller
(on the neo side).

Application is written in Python/Elementary.
python-elementary version must be 0.1+svnr39575 or greater :-(
This app works fine under SHR-unstable. For other distributions, I fear
that it does not.

I don't know if some people will find this app useful.
But if it's the case, lets me know.
I'm not sure to be able to find time in the future to improve this
program, but I can:
- modify it to accept GTK as toolkit for GUI (instead of Elementary)
- create a project (anti-buzz ?) on http://projects.openmoko.org
- create a package for opkg.org ?


Description
===========
The application reads mixer settings (controls) in
/usr/share/openmoko/scenarios/gsmhandset.state
and for 5 of them (5, 12, 48, 4, 6) displays a slider to adjust their
values.
NEVER the app modify the file
/usr/share/openmoko/scenarios/gsmhandset.state

Button "Apply": load current mixer settings in the soundcard and create
an new gsmhandset.state file in /tmp.

Button "Restore default": at any time, you can click on this button to
restore mixer settings in the app and in the soundcard.

Usage
=====
* Before or during a call, launch the app:
   ./anti-buzz.py
* Changes settings with sliders
* Click on 'Apply' button to test changes in live
* When you are happy with a set of settings, copy /tmp/gsmhandset.sate
   in /usr/share/openmoko/scenarios/


To finish, here the settings which go best with my FR:
* Mono Playback Volume (5): 112
* Mono Sidetone Playback Volume (12): 5
* Mic2 Capture Volume (48): 2

* Speaker Playback Volume (4): 112
* Bypass Playback Volume (6): 7

--
Valéry Febvre
#!/usr/bin/env python
# -*- coding: utf-8 -*-

ALSA_STATE_PATH = '/usr/share/openmoko/scenarios/gsmhandset.state'

# define here controls you want to manage
# only controls of type INTEGER are allowed
CONTROLS = [
    5, 12, 48, # mic volume & buzz problem
    4, 6,      # speaker volume & echo problem
]

################################################################################
# Anti-Buzz -- an application to kill your GSM buzz
#
# Copyright (C) 2009 Valéry Febvre <[email protected]>
# http://???.projects.openmoko.org/
#
# This file is part of Anti-Buzz.
#
# Neon is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Neon 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.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Anti-Buzz is an application to kill your GSM buzz.
"""

import os, re, commands
import elementary

RE_CONTROL = re.compile(r'^\t\control.(\d+) {$')

RE_TYPE    = re.compile(r'^\t\tcomment.type ([A-Z]+).*')
RE_COUNT   = re.compile(r"^\t\tcomment.count (\d+)")
RE_RANGE   = re.compile(r"^\t\tcomment.range '(\d+) - (\d+)")
RE_NAME    = re.compile(r"^\t\tname '([A-Za-z0-9- ]+)'")
RE_VALUE   = re.compile(r'^\t\tvalue (\d+)')
RE_VALUE0  = re.compile(r'^\t\tvalue.0 (\d+)')
RE_VALUE1  = re.compile(r'^\t\tvalue.1 (\d+)')

controls_data = {}
blocks        = {}
sliders       = {}

def destroy(obj, event, data):
    print "Bye bye"
    elementary.exit()

def restore_default(obj, event, data):
    for id in sliders.keys():
        control = controls_data[id]
        sliders[id].value = float(control['value'])
    alsactl_restore(ALSA_STATE_PATH)

def apply_changes(obj, event, data):
    new_alsa_state_path = '/tmp/%s' % os.path.basename(ALSA_STATE_PATH)
    f = open(new_alsa_state_path, 'w+')
    f.write("state.neo1973gta02 {\n")
    for id in blocks.keys():
        for line in blocks[id]:
            if id in CONTROLS and \
                (RE_VALUE.match(line) or RE_VALUE0.match(line) or RE_VALUE1.match(line)):
                old_value = controls_data[id]['value']
                new_value = str(int(round(sliders[id].value)))
                #print 'control %d: %s => %s' % (id, old_value, new_value)
                line = line.replace(old_value, new_value)
            f.write(line)
    f.close()
    alsactl_restore(new_alsa_state_path)
    msg = "New Alsa scenario has been applied and saved in %s" % new_alsa_state_path
    label_info.label_set(msg)

def alsactl_restore(state_file):
    commands.getoutput('alsactl -f %s restore' % state_file)

if __name__ == "__main__":
    f = open(ALSA_STATE_PATH, 'r')
    lines = f.readlines()

    num = None

    # store lines of all controls
    for line in lines:
        if RE_CONTROL.match(line):
            num = int(RE_CONTROL.findall(line)[0])
            blocks[num] = []
        if num:
            blocks[num].append(line)

    # get data of selected controls
    for id in CONTROLS[:]:
        is_valid = True
        count = None
        type_ = None
        for line in blocks[id]:
            # type
            if RE_TYPE.match(line):
                type_ = RE_TYPE.findall(line)[0]
            if type_ and type_ != 'INTEGER':
                print 'WARNING: ignore control %d of type %s' % (id, type_)
                del CONTROLS[CONTROLS.index(id)]
                is_valid = False
                break

            # name
            if RE_NAME.match(line):
                name = RE_NAME.findall(line)[0]
            # count
            if RE_COUNT.match(line):
                count = int(RE_COUNT.findall(line)[0])
            # range
            if RE_RANGE.match(line):
                range = RE_RANGE.findall(line)[0]
            # value or value0
            if count == 1:
                if RE_VALUE.match(line):
                    value = RE_VALUE.findall(line)[0]
            elif count == 2:
                if RE_VALUE0.match(line):
                    value = RE_VALUE0.findall(line)[0]

        if is_valid:
            controls_data[id] = {
                'name':  name,
                'type':  type_,
                'count': count,
                'range': range,
                'value': value,
                }

    # build GUI
    elementary.init()
    win = elementary.Window("test", elementary.ELM_WIN_BASIC)
    win.title_set("anti-buzz application")
    win.destroy = destroy
    
    bg = elementary.Background(win)
    win.resize_object_add(bg)
    bg.size_hint_weight_set(1.0, 1.0)
    bg.show()
    
    box0 = elementary.Box(win)
    box0.size_hint_weight_set(1.0, 1.0)
    win.resize_object_add(box0)
    box0.show()

    sc = elementary.Scroller(win)
    sc.size_hint_weight_set(1.0, 1.0)
    sc.size_hint_align_set(-1.0, -1.0)
    box0.pack_end(sc)
    sc.show()

    box1 = elementary.Box(win)
    box1.size_hint_weight_set(1.0, 1.0)
    sc.content_set(box1)
    box1.show()

    for id in CONTROLS:
        if id not in controls_data.keys():
            continue

        control = controls_data[id]

        label = elementary.Label(win)
        label.label_set(' <b>%s</b> (%d)' % (control['name'], id))
        label.size_hint_align_set(0, 0.5)
        label.scale_set(1.5)
        box1.pack_end(label)
        label.show()

        slider = elementary.Slider(win)
        slider.label_set('')
        slider.span_size_set(160)
        slider.size_hint_align_set(0, 0.5)
        slider.size_hint_weight_set(0, 1)
        slider.unit_format_set("   %3.0f" + ' / %d' % float(control['range'][1]))
        slider.indicator_format_set("%3.0f")
        slider.min_max_set(float(control['range'][0]), float(control['range'][1]))
        slider.value = float(control['value'])
        slider.scale_set(2)
        box1.pack_end(slider)
        slider.show()
        sliders[id] = slider

    label_info = elementary.Label(win)
    label_info.label_set(' ')
    label_info.size_hint_align_set(0.5, 0.5)
    box0.pack_end(label_info)
    label_info.show()

    table = elementary.Table(win)
    table.size_hint_weight_set(1, -1)
    box0.pack_end(table)
    table.show()

    btn_default = elementary.Button(win)
    btn_default.label_set('Restore Default')
    table.pack(btn_default, 0, 0, 1, 1)
    btn_default.show()
    btn_default.clicked = restore_default

    btn_apply = elementary.Button(win)
    btn_apply.label_set('     Apply     ')
    table.pack(btn_apply, 1, 0, 1, 1)
    btn_apply.show()
    btn_apply.clicked = apply_changes

    btn_quit = elementary.Button(win)
    btn_quit.label_set('      Quit      ')
    table.pack(btn_quit, 2, 0, 1, 1)
    btn_quit.show()
    btn_quit.clicked = destroy

    win.resize(480, 640)
    win.show()
    elementary.run()
    elementary.shutdown()

<<inline: Screenshot-1.jpg>>

_______________________________________________
Openmoko community mailing list
[email protected]
http://lists.openmoko.org/mailman/listinfo/community

Reply via email to