Cabinet frequency collisions software tool

I've written a simple python program, in a git er done brute force and ignorance manner, which indicates where frequency collisions occur in a cabinet given the width, height, and depth (in millimeters). I'll post below. The idea is that each dimension has harmonic frequencies, when one or both of the other dimensions have similar harmonic frequencies then that frequency is prone to nulls (or possibly boost). The program identifies these collisions and there is a very simple optimizer that looks at changing dimensions to reduce the number of collisions.

Example exec: py ./main.py 208 968 363
208 is width in mm, 968 height, 363 depth


Looking for feedback on logic/calculation errors. Thanks.

import sys

# Copyright 2023,2024 Aaron Loyd
#
# 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.


def dimensionHarmonics(dimension):
harmonics = []
# speed of sound in mms 342900
hfirst = round(((342900 / int(dimension)) / 2))
harmonics.append(hfirst)
for harmonic in range(2, 10):
harmonics.append(hfirst * harmonic)
# for value in wharmonics:
# print(value)
return (harmonics)


def swcalc(wharmonics, hharmonics, dharmonics):
whcollisions = []
wdcollisions = []
hdcollisions = []
for windex in range(9):
# diffval = 0.05 * wharmonics[windex]
diffval = 50
for hindex in range(9):
if ((abs(int(wharmonics[windex]) - int(hharmonics[hindex]))) < diffval):
whcollisions.append(str(wharmonics[windex]) + " with " + str(hharmonics[hindex]))
if ((abs(int(wharmonics[windex]) - int(dharmonics[hindex]))) < diffval):
wdcollisions.append(str(wharmonics[windex]) + " with " + str(dharmonics[hindex]))

for hindex in range(9):
# diffval = 0.05 * int(hharmonics[hindex])
diffval = 50
for dindex in range(9):
if ((abs(int(hharmonics[hindex]) - int(dharmonics[dindex]))) < diffval):
hdcollisions.append(str(hharmonics[hindex]) + " with " + str(dharmonics[dindex]))

return whcollisions, wdcollisions, hdcollisions


def printCollisions(whcollisions, wdcollisions, hdcollisions):
print("----------------------------------------------------------------------\n\n")
print("Width Collisions with Height")
if (len(whcollisions) <= 0):
print("none")
else:
for whc in whcollisions:
print(whc)

print("----------------------------------------------------------------------")
print("Width Collisions with Depth")

if (len(wdcollisions) <= 0):
print("none")
else:
for wdc in wdcollisions:
print(wdc)

print("----------------------------------------------------------------------")
print("Height Collisions with Depth")

if (len(hdcollisions) <= 0):
print("none")
else:
for hdc in hdcollisions:
print(hdc)

print("----------------------------------------------------------------------\n\n")


def checkCollisions(owidth, oheight, odepth, totalCollisionCount):
owharmonics = dimensionHarmonics(owidth)
ohharmonics = dimensionHarmonics(oheight)
odharmonics = dimensionHarmonics(odepth)

owhcollisions, owdcollisions, ohdcollisions = swcalc(owharmonics, ohharmonics, odharmonics)
collisionCount = len(owhcollisions) + len(owdcollisions) + len(ohdcollisions)
if (collisionCount < totalCollisionCount):
print("\nusing width of " + (str(owidth)))
print("using height of " + (str(oheight)))
print("using depth of " + (str(odepth)))
print("collision count is " + str(collisionCount) + " instead of " + str(totalCollisionCount))
print("\n")
printCollisions(owdcollisions, owdcollisions, ohdcollisions)


def optimize(width, height, depth, totalCollisionCount) :
print("----------------------------------")
print("optimizing")
origvolume = width * height * depth
maxvolume = origvolume * 1.02
minvolume = origvolume * 0.98

# width check
print("\nexpand width")
volume = origvolume
owidth = width
oheight = height
odepth = depth

while (volume < maxvolume):
print(".", end='')
owidth += 1.0

volume = owidth * oheight * odepth

checkCollisions(owidth, oheight, odepth, totalCollisionCount)

print("\nshrink width")
volume = origvolume
owidth = width
oheight = height
odepth = depth

while (volume > minvolume):
print(".", end='')
owidth -= 1.0

volume = owidth * oheight * odepth

checkCollisions(owidth, oheight, odepth, totalCollisionCount)

# height check
print("\nexpand height")
volume = origvolume
owidth = width
oheight = height
odepth = depth
while (volume < maxvolume):
print(".", end='')
oheight += 1.0

volume = owidth * oheight * odepth

checkCollisions(owidth, oheight, odepth, totalCollisionCount)

print("\nshrink height")
volume = origvolume
owidth = width
oheight = height
odepth = depth

while (volume > minvolume):
print(".", end='')
oheight -= 1.0

volume = owidth * oheight * odepth

checkCollisions(owidth, oheight, odepth, totalCollisionCount)

# depth check
print("\nexpand depth")
volume = origvolume
owidth = width
oheight = height
odepth = depth
while (volume < maxvolume):
print(".", end='')
odepth += 1.0

volume = owidth * oheight * odepth

checkCollisions(owidth, oheight, odepth, totalCollisionCount)

print("\nshrink depth")
volume = origvolume
owidth = width
oheight = height
odepth = depth

while (volume > minvolume):
print(".", end='')
odepth -= 1.0

volume = owidth * oheight * odepth

checkCollisions(owidth, oheight, odepth, totalCollisionCount)

print("\noptimizing finished.")


if name == 'main':
print("Collisions can occur at the following frequencies:")
print("Width\t\t\tHeight\t\t\t\tDepth")

# dimensions in millimeters
width = float(sys.argv[1])
height = float(sys.argv[2])
depth = float(sys.argv[3])

wharmonics = dimensionHarmonics(width)
hharmonics = dimensionHarmonics(height)
dharmonics = dimensionHarmonics(depth)

for index in range(9):
print(str(wharmonics[index]) + "\t\t\t\t" + str(hharmonics[index]) + "\t\t\t\t" + str(dharmonics[index]))

whcollisions, wdcollisions, hdcollisions = swcalc(wharmonics, hharmonics, dharmonics)
printCollisions(whcollisions, wdcollisions, hdcollisions)

totalCollisionCount = len(whcollisions) + len(wdcollisions) + len(hdcollisions)

print("total collisions = " + str(totalCollisionCount))
print("----------------------------------")

totalWidthCollision = len(whcollisions) + len(wdcollisions)
print("total width collisions = " + str(totalWidthCollision))

totalHeightCollision = len(whcollisions) + len(hdcollisions)
print("total height collisions = " + str(totalHeightCollision))

totalDepthCollision = len(wdcollisions) + len(hdcollisions)
print("total depth collisions = " + str(totalDepthCollision))

if (totalCollisionCount > 0):
optimize(width, height, depth, totalCollisionCount)
 
Frequencies aren't simply related to the external dimensions, there are end-effects that depend on the thickness of the walls, and the stiffness of joints along the edges will affect things too. For wood the material properties vary depedent on the grain direction and across the sample - best to measure these things to back up the calculations I would suggest.
 
It is hard to make suggestions without an understanding of the purpose of the program. Why would I want to know how many frequencies in different directions are close to each other? If they are would the negative effect on performance (which I cannot see) be sufficiently serious to warrant moving away from speaker dimensions which will have been determined by things like baffle shape, driver size, ear height, etc... that do have a significant effect on performance?

Related information I might want to know are the lowest frequency/ies because this may influence the amount of stuffing required and it's location. I might want to know the mode shape of the lowest frequencies to help with driver and port location. I might want to know if any port frequencies match any cavity frequencies. This is pretty detailed information though that many won't trouble themselves with because it will have only a minor if any influence on the performance.

Of course the distribution of modal frequencies and their mode shapes in a room is pretty much required information if one is to tackle sound quality at low frequencies in an effective manner but this is much the less case with a cabinet including a reasonable placement of stuffing to reduce the internal cabinet resonances to negligible levels.

As someone with a strong interest in simulation and programming I would like to be supportive and like to see more of it in the hobby. Would you like comments about the programming or just the results?
 
Last edited:
First, this is not about cabinet resonance.

Second, most people seem to recognize that a cube is a bad choice of cabinet shape because it causes so many nulls in frequency. But, I get the sense that many believe that as long as the cabinet is not a cube then it is okay - this is not true because certain dimensions can still lead to numerous nulls (only slightly better than a cube).

Apparently, this is not something that speaker builders will find useful. I will continue to use the tool to optimize my cabinets to have none or at most one null collision in only two dimensions.

If anyone comes across this post and wants an updated version just PM me.
 
Second, most people seem to recognize that a cube is a bad choice of cabinet shape because it causes so many nulls in frequency.

I think you will find that only a few people think that a cube is a bad shape because of cabinet cavity resonances being close in frequency. Just as only a few think that non-parallel walls are relevant in reducing the negative effect of internal cabinet resonances. They certainly exist and they may find your program useful but I doubt most hobbyists will just as most hobbyists almost certainly don't find detailed 3d simulation software useful in the way I and a small number of other hobbyists do. It's a hobby and people are interested in what they are interested in.

But, I get the sense that many believe that as long as the cabinet is not a cube then it is okay - this is not true because certain dimensions can still lead to numerous nulls (only slightly better than a cube).

Not if the cabinet contains stuffing to absorb the resonances which is a feature of well designed speaker cabinets with perhaps the odd exception like a small subwoofer cabinet.

Apparently, this is not something that speaker builders will find useful.

Small programs/scripts can be useful if they address something of interest. If you were to extend yours to include other aspects of cabinet cavity resonances interest would likely increase.