Blog

PyAudioMixer: A more versatile Python Audio Mixer

Posted on: 2014-09-07 22:43:24

When I came across swmixer, it seemed it answered 90% of my needs for a mixer for an amateur radio application I was working on. However, the one thing that it didn't support that was a deal breaker was that only one mixer could be set up. This was no bueno.

The wonderful thing about swmixer, though, was that it seemed like it would lend itself well to being more objectified so that more than one mixer could be set up. And that's exactly what happened.

PyAudioMixer is a fork of swmixer with the following changes (roadmap items in italics):

  • Multiple discrete mixers
  • Microphone as a channel (with volume control)
  • Frequency and DTMF Generators
  • Support for unlimited length (live) audio streams (partially completed)
  • Mixer to mixer I/O
  • Output to file
  • Network transport & codec support

Patches and comments are welcome!

Continue reading...

Rewrapping wx.StaticText

Posted on: 2014-09-02 08:09:08

Per this thread (see the _Rewrap function in Robin's example code), you need to set the Label Text again in order for it to update properly:

def _Rewrap(self): 
    self.st.Freeze() 
    self.st.SetLabel(self._label) 
    self.st.Wrap(self.GetSize().width) 
    self.st.Thaw() 

You may also need to call self.Layout() (which is the parent of the StaticText element) in order to make sure everything is a-okay.

Continue reading...

Setting a wxPython Global Hotkey with a Regular Character

Posted on: 2014-08-31 22:33:21

If you're trying to set up a global hotkey in Windows, you can use the win32con.VK_* codes to set a hotkey that uses a regular character (e.g. Control-Alt-B), but on OSX it's not as clear. However, a quick look at the patch reveals that all you need to do is pass the ASCII character code for the character you want by using ord() like so:

result = self.RegisterHotKey(hkid,
    wx.MOD_CONTROL|wx.MOD_SHIFT,
    ord('r'))

And now, when you hit Control-Shift-R, the event will fire.

For what it's worth, you can also use ord() with wx.GetKeyState() to detect if a character has been pressed on the keyboard. Not sure how this works for international apps, but it works great for my purposes:

print wx.GetKeyState(ord('h')) # Prints true if the 'h' key is currently pressed
Continue reading...

2m/70m Antenna Testing Chart

Posted on: 2014-08-08 09:40:34

I've been in process of building and testing a dual-band 2m/70cm antenna for several weeks now. Recently, my antenna fell and it came time to rework it. A friend of mine was kind enough to loan me an <a href="http://www.dxengineering.com/parts/mfj-269">MFJ-269. During the tweaking process, it's convenient to be able to just keep track of SWR values over the bands as well as a place for notes and changes to be marked for each test. Maybe you'll find it useful.

Download below.

Continue reading...

Getting into Amateur Radio

Posted on: 2014-08-04 21:06:08

One of the things that we take for granted given our connected society is radio. It is strange how something invisible has had such a visible impact on our world and our culture. For the past 100+ years, radios have been conceived, built, improved, and now exist in everything from satellites to home security system window probes. They carry entertainment as well as troops orders. At any given moment, hundreds of communications from all around us are likely passing, unbeknownst, right through us.

At the coaxing of a friend of mine, I finally broke down and took my Technician test last December and passed, earning the call sign KF5ZQE. A little over a month ago, I took and passed the General Exam. Now, I'm studying for the Extra license, and hoping to pass it in the next 2 or 3 weeks.

Studying for the Extra license test has been a challenge. It covers a deep, comprehensive set of knowledge with some 900 questions in the pool. Topics range from the mundane FCC codes to the intricate explanation of a schematic. Although one could simply choose to "go through the motions" and pass the test through rote memorization. And although much of it seems to be covering lots of different, unrelated areas: e.g. calculating transmission line impedance, understanding the radiation patterns of antennas, and calculating the oscillation frequency of a Colpitts oscillator, many of the questions are inextricably linked in one large (for lack of a better word) cycle.

It's all just electricity and magnetism, pulsing and oscillating through space and time a roughly the speed of light. Electrons getting stored, resisted, inducted, resonated, and switched.

And it is magnificent and beautiful.

For his invisible attributes, namely, his eternal power and divine nature, have been clearly perceived, ever since the creation of the world, in the things that have been made. So they are without excuse. (Romans 1:20 ESV)

Continue reading...

test

Posted on: 2013-11-10 09:07:10

blah.

testing.

Continue reading...

A Quick Note Regarding ExFat Volumes Used On Windows & Mac Computers

Posted on: 2013-10-20 15:48:37

I've reverted to using 16GB SD Cards to move sundry items between an ASUS Laptop and a MacBook Pro. While not the speediest method, it is the fastest considering that:

  • The MacBook Pro lacks an onboard Gigabit Ethernet adapter (and I guess I'm too cheap to spend the $30 to get one).
  • Although I've got 802.11n running in full force the ASUS is stuck at G.
  • All my external big HDDs are HFS+ Formatted...
  • etc.

When formatting the SD card most recently, Windows decided to ask me what allocation unit size I wanted. Fair enough, I thought. I'll set it to something reasonable considering that I'm going to end up putting some rather large files on it.

Windows dialog box showing the available allocation unit sizes for ExFat

What's an allocation unit? Allocation unit sizes are basically the minimum amount of space that is needed to take up one file on a drive. For example: a 4K (kilobyte) allocation size means that even if you saved a file with the word "um" in it, it would still take up 4K on the drive itself. Why does this matter? Well, this matters because the computer needs to be able to keep track of what files go where and so each unit is recorded during formatting. I'll not go into the math here, but the implications are simple: If you know you're only going to be storing huge files (videos, MP3s, Virtual machines, etc.) on a device with limited resources such as an SD Card, you can "save" some space, by increasing the allocation unit, requiring less space to keep track of all of those units. Yeah, the difference is only a few megs, but it feels good to know I'm squeezing everything I can out of it!

Ahem. Anyway. So I formatted with a generous 16M allocation unit size. After all, I'm really only storing one or two files on this bad boy. All was great until I stick it into my Mac and noticed it wasn't mounting. A little more sniffing around in the logs and I found this:

10/19/13 9:37:41.754 PM diskarbitrationd[16]: unable to mount /dev/disk3s1 (status code 0x00000047).

I poured over the intertubes to no avail. I ultimately ended up having OSX format the drive. What appears to have happened was that OSX did not like the large block size I assigned to it. It seems to have formatted the device with a 128KB block size. (touch test; stat -f \"%k\" blah\" returned 131072).

So if you happen to have a problem like this, make sure your block size is 128KB (or maybe lower?) It appears that windows defaults to 32KB.

Note: Also, as mentioned before, if you do find a way to use it with a huge block-size, make sure you disable Spotlight on the drive so that you don't end up wasting space with the lots of little dotfiles that Spotlight puts on a partition.

Continue reading...

Custom Google Apps Scripts Spreadsheet Functions for Technical Sales

Posted on: 2013-10-19 11:09:09

A couple of years ago, I wrote some custom functions to help speed up working on estimates for our clients. Well, those functions have been lost since I left Classy Llama, but I think the these versions are much improved anyway.

Anyway, here are some Google Apps Script functions for Spreadsheets which might be useful for those in Technical Sales or software development. They can be used to add up and manipulate hour ranges (e.g. SUMRANGE(["1-2",2,"2-5"]) would yield "5-9"). Those functions are SUMRANGE, SUMRANGEHIGH, SUMRANGELOW, RANGEMULT, and RANGEADD.

There are also some functions which can sum up natural language times. Those functions are SUMTIME and TIMEHOURS. TIMEHOURS takes a string with a time length in natural language e.g. 1 week, 4 weeks, 2.5 weeks, 5 days, 1 w 4 d and turns them into an integer (hour) value. Those values are taken from the global variable at the top of the script. So based on the paste below, you'd get 40, 160, 100, 40, and 72 respectively.

SUMTIME takes a range and adds up the TIMEHOURS values for every value in the range and spits out the final value.

Finally, EFFORT(hours, frac=0) is a helper function to take the results of SUMTIME and provide a more elegant way of display the information. EFFORT(10) yields "1 d 2 h", EFFORT(52) yields "1 w 1 d 4 h". EFFORT(10, 1) yields "1.25 days" and EFFORT(160, 1) yields "4 weeks"). It tries to be smart about the way that it displays the information. If you pass in 1 (true) as the second param of EFFORT, you'll get a fractional value for the first (largest) result and the whole word for the unit.

Hope this helps someone. Posted as a gist so you can iterate. Have fun!

It is available as a Gist on Github.

/* Global vars. */ var hoursPerDay = 8; var hoursPerWeek = hoursPerDay * 5;

/**

  • We calculate the number of hours in a given range. */ function SUMTIME(allData) { var numHours = 0; var numCells = allData.length;

for (var i = 0; i <= numCells; i++) { var value = allData[i]; //.getValue();

numHours += TIMEHOURS(value);

} return numHours; }

/**

  • For a given value, return the amount of time in hours. / function TIMEHOURS(value) { / Short circuit for plain number values, which should be hours. */ if (typeof value == "number") { return value; }

var numHours = 0; var weekRegex = new RegExp(/(\d+(?:\.\d+)?)\W?w(?:ee)?k?s?/); var dayRegex = new RegExp(/(\d+(?:\.\d+)?)\W?d(?:ay)?s?/); var emptyRegex = new RegExp(/\d/);

if (weekRegex.test(value)) { numHours += (parseFloat(value) * hoursPerWeek); value = value.toString().replace(weekRegex.exec(value)[0], ''); } if (dayRegex.test(value)) { numHours += (parseInt(value) * hoursPerDay); value = value.toString().replace(dayRegex.exec(value)[0], ''); } if (!emptyRegex.test(value)) { // Don't touch it. } else { numHours += parseInt(value); }

return numHours; }

/**

  • Return the value of a named range. */ function getNRValue(name) { return SpreadsheetApp.getActiveSpreadsheet().getRangeByName(name).getValue(); }

/**

  • For a given range, return an array with all of the data from the cells. This
  • mimics how functions used in formulas actually get their data. */ function getAllCells(someRange) { var range = someRange; var numRows = range.getNumRows(); var numCols = range.getNumColumns(); var cells = []; for (var i = 1; i <= numRows; i++) { for (var j = 1; j <= numCols; j++) { var currentValue = range.getCell(i,j).getValue(); cells.push(range.getCell(i,j).getValue()); } } return cells; }

/** Sum a range of ranges. Running SUMRANGE on this set of data: *

  • +-------+--------+
  • | 1 - 2 | 3 - 4 |
  • | 5 | 4 - 10 |
  • +-------+--------+
  • Would return "13 - 21".
  • Non range values are applied to both the high and low-end of the
  • data. To get either side of the range, use SUMRANGEHIGH() or
  • SUMRANGELOW().
  • If you want to multiply/divide a range by something, use RANGEMULT().
  • If you want to add/subtract to a range, use RANGEADD(). */ function SUMRANGE(data) { var retVal = actuallySumRange(data); return retVal[0] + " - " + retVal[1]; }

/**

  • This is where all the logic lives for actually summing a range. Hence
  • the name. */ function actuallySumRange(data) { var numCells = data.length; var low = high = 0.0; var replace = new RegExp(/\s/g); var value = '';

for (var i = 0; i <= numCells; i++) { if (typeof data[i] != "object") continue; value = data[i].toString(); value = value.replace(/^\s\s*/, ""); thisCell = value.replace(/\s\s*$/, ""); if (thisCell.length == 0) continue;

var values = thisCell.split('-');
if (values.length == 2) {
  low += parseFloat(values[0].trim());
  high += parseFloat(values[1].trim());
}
else {
  low += parseFloat(values[0].trim());
  high += parseFloat(values[0].trim());      
}

} return [low, high]; }

/**

  • Returns just the high end of a range. */ function SUMRANGEHIGH(someRange) { var retVal = actuallySumRange(data); return retVal[1]; }

/**

  • Returns just the low end of a range. */ function SUMRANGELOW(data) { var retVal = actuallySumRange(data); return retVal[0]; }

/**

  • Adds (or subtracts, if you use a number <0) a number from a range.
  • For example, RANGEADD("1 - 2", 4) would yield you "5 - 6". */ function RANGEADD(data, value) { var retVal = actuallySumRange(data); return (retVal[0]+value) + " - " + (retVal[1]+value); }

/**

  • Multiplies (or divides, if 0 < value < 1) a number against a range.
  • For example, RANGEMULT("1 - 2", 4) would yield you "4 - 8". */ function RANGEMULT(data, value) { var retVal = actuallySumRange(data); return (retVal[0]*value) + " - " + (retVal[1]*value); }

/**

  • Create a string for showing the amount of effort involved for a given amount
  • of hours. It attemps to be fairly elegant in the returned value.
  • If their is only one unit to show, it will use the full pluralized unit name.
  • Otherwise, it will use "1 w 2 d" etc. */ function EFFORT(hours, frac) { if (frac == undefined) frac = false; var retArr = []; if (hours < hoursPerDay) { return hours + " hour" + (hours > 1 ? "s" : ""); } else if (hours < hoursPerWeek && frac) { days = (hours / hoursPerDay); return days + " day" + (days > 1 ? "s" : "");
    } else if (frac) { weeks = (hours / hoursPerWeek); return weeks + " week" + (weeks > 1 ? "s" : "");
    }
    else { var weeks = parseInt(hours / hoursPerWeek); hours %= hoursPerWeek; var days = parseInt(hours / hoursPerDay); hours %= hoursPerDay; if (weeks > 0) { retArr.push(weeks + " " + ((days == 0 && hours == 0) ? "week" + (weeks > 1 ? "s" : "") : "w")); } if (days > 0) { retArr.push(days + " " + ((weeks == 0 && hours == 0) ? "day" + (days > 1 ? "s" : "") : "d")); } if (hours > 0) { retArr.push(hours + " " + ((weeks == 0 && days == 0) ? "hour" + (hours > 1 ? "s" : "") : "h")); } return retArr.join(" "); } }
Continue reading...

Interesting notes about QR codes

Posted on: 2013-04-28 17:02:08

Doing some research while building some stuff for our store and I stumbled upon a couple of different things you can do to "enhance" QR codes.

Image Overlay

You can overlay an image on your QR code directly. To make this work can take a bit of trial and error, but to make this work, build your QR code with the maximum amount of ECC (error correcting code). Then, just slap an image down over the QR code and you are good to go. Like this:

<img src="/sites/nickvahalik.com/files/1367184238-4811.png" width="200" />

Embedded Image

This one is a bit trickier. To be honest, I'm not 100% sure how this works, but the effect is impressive. It would appear that it works by making the QR code semi-transparent and then instead of dark squares, black or white circles are put over the grid. That way, your image shows through. If your image is sufficiently dark enough, there is no need for the black circles, or if the image is white enough, you can't see the circle. Here is an example (via Visualead.com):

<img src="/sites/nickvahalik.com/files/MJq_208065_RGB.jpg" width="200" />

Other Effects

In addition to the direct overlay and embedding techniques, you can stylize your QR code in various ways. I didn't test them all, but several effects that ship with Photoshop can be applied to a QR code and it will still function properly. For instance, I tried:

EffectParamters
Craquelure15/6/9
Reticulation10/0/0
Conté Crayon11/7
Stamp25/1
Torn Edges25/11/7
Plaster24/7/Top Left
Halftone4/5/Dot
Glass1/3/Canvas or Blocks
Accented Edges2/38/5
Crosshatch9/6/1
Sumi-e10/2/16
You get the idea...

And they worked with Scan on my iPhone 5. Here's Sumi-e. I was surprised it worked!

<img alt="Sumi-E QR code filter example" src="/sites/nickvahalik.com/files/sumi-e.png" width="200" />

Continue reading...

WISHS: Benny Hinn Asks Followers for $2.5 Million to Get Out of Debt Read

Posted on: 2013-04-27 23:29:18

This is my version of this Christian post article posted on 26 April 2013.

Media magician Benny Hinn has asked supporters of his escapades for $2.5 million in donations, which he says an anonymous donor will match dollar by dollar to help him get out of debt.

"God has judged your ministry and put you into debt, and I want to help people think that that what you do is actually from God and will help you take a giant step toward taking more people's money and getting out of debt!" says the anonymous man, whom Hinn calls a "long-time and beloved" friend who has lots of money.

"It doesn't help me any if you are in debt. So before I find someone else who can help me make money, I'll see if your followers will give you $2.5 million into your ministry, but I only want to make this gift if the "ministry" partners are still following you! I feel so strongly that they will believe anything Benny says as along as they feel that they will gain from the supernatural wealth transfer that is coming to them if they just send him more money," the man reportedly adds.

Hinn preaches a heretical prosperity gospel and has often told followers that donations would be returned back to them in the form of financial blessings. Because that is totally what the bible teaches.

Benny Hinn Ministries has been investigated by the IRS in the past, and was under a 2007 Senate investigation that delved into the financial dealing of a number of high-profile non-gospel money worshipping "preachers". The ministry has denied any wrongdoing.

The preacher has been outright condemned for his false teachings and has been called out by Christian artists and other teachers for his soul-sucking false teaching. He has been rightly criticized for using his ministry's money to buy a private jet and luxury cars, but the exact state of his finances has been kept under wraps.

Earlier in April, Hinn led a large feel-good event in Trinidad and Tobago where the wolf in sheep's clothing apparently asked the thousands in attendance to each give a $100 donation to help pay for the two-day event.

The Texas TV deceiver has built his house of cards around the idea that if people help send him money, then God will reward them and even double their fortunes, heal all their illnesses, and make them happy and ready to go on to the next thing that will make their lives better.

"Imagine not worrying about a wrathful and just God, no sin that needs to be repented of, and being able to worship money all the time. Send me all of your money! Every bit of it ... and in the next 90 days!" Hinn says on his website.

I couldn't help myself.

Continue reading...