The [simple_sound] code will probably go into my ch 3 at <url:
http://tinyurl.com/programmingbookP3>, but sans sine wave generation since I
haven't yet discussed trig functions, and maybe /with/ changes suggested by you?
Module:
<code file="simple_sound.py">
"Lets you generate simple mono (single-channel) [.wav], [.aiff] or [.aifc] files."
import collections
import array
import math
DataFormat = collections.namedtuple( "DataFormat",
"open_func, append_int16_func"
)
default_sample_rate = 44100 # Usual CD quality.
def sample_square( freq, t ):
linear = freq*t % 1.0
if linear < 0.5:
return -1.0
else:
return 1.0
def sample_sawtooth( freq, t ):
linear = freq*t % 1.0
if linear < 0.5:
return 4.0*linear - 1.0
else:
return 3.0 - 4.0*linear
def sample_sine( freq, t ):
return math.sin( 2*math.pi*freq*t )
def _append_as_big_endian_int16_to( a, i ):
if i < 0:
i = i + 65536
assert( 0 <= i < 65536 )
a.append( i // 256 )
a.append( i % 256 )
def _append_as_little_endian_int16_to( a, i ):
if i < 0:
i = i + 65536
assert( 0 <= i < 65536 )
a.append( i % 256 )
a.append( i // 256 )
def aiff_format():
import aifc
return DataFormat( aifc.open, _append_as_big_endian_int16_to )
def wav_format():
import wave
return DataFormat( wave.open, _append_as_little_endian_int16_to )
class Writer:
"Writes normalized samples to a specified file or file-like object"
def __init__( self, filename, sample_rate = default_sample_rate,
data_format = aiff_format() ):
self._sample_rate = sample_rate
self._append_int16_func = data_format.append_int16_func
self._writer = data_format.open_func( filename, "w" )
self._writer.setnchannels( 1 )
self._writer.setsampwidth( 2 ) # 2 bytes = 16 bits
self._writer.setframerate( sample_rate )
self._samples = []
def sample_rate( self ):
return self._sample_rate
def write( self, normalized_sample ):
assert( -1 <= normalized_sample <= +1 )
self._samples.append( normalized_sample )
def close( self ):
data = array.array( "B" ) # B -> unsigned bytes.
append_int16_to = self._append_int16_func
for sample in self._samples:
level = round( 32767*sample )
append_int16_to( data, level )
self._writer.setnframes( len( self._samples ) )
self._writer.writeframes( data )
self._writer.close()
</code>
By the way, the reason that it holds on to data until 'close' and does the
writing there is to work around a bug in [wave.py]. That bug's now corrected but
wasn't when I wrote above. And possibly best to keep it like it is?
Ideally should deal with exceptions in 'close', calling close on the _writer,
but I haven't yet discussed exceptions in the hopefully-to-be book writings
where this probably will go.
Example usage, illustrating that it's simple to use (?):
<code file="aiff.py">
import simple_sound
sample_rate = simple_sound.default_sample_rate
total_time = 2
n_samples = sample_rate*total_time
writer = simple_sound.Writer( "ringtone.aiff" )
for i in range( n_samples ):
t = i/sample_rate
samples = (
simple_sound.sample_sine( 440, t ),
simple_sound.sample_sine( (5/4)*440, t ),
)
sample = sum( samples )/len( samples )
writer.write( sample )
writer.close()
</code>
Utility class that may be used to capture output (an instance of this or any
other file like class can be passed as "filename" to simple_sound.Writer):
<code>
class BytesCollector:
def __init__( self ):
self._bytes = array.array( "B" )
self._pos = 0
def raw_bytes( self ):
return self._bytes
def bytes_string( self ):
return self._bytes.tostring()
# File methods:
def tell( self ):
return self._pos
def seek( self, pos, anchor = 0 ):
assert( anchor == 0 ) # Others not supported
assert( pos <= len( self._bytes ) )
self._pos = pos
def write( self, bytes ):
pos = self._pos
if pos < len( self._bytes ):
s = slice( pos, pos + len( bytes ) )
self._bytes[s] = bytes
self._pos = s.stop
else:
self._bytes.extend( bytes )
self._pos = len( self._bytes )
def flush( self ):
pass
def close( self ):
pass
</code>
Cheers & enjoy,
- Alf
PS: Comments welcome, except the BytesCollector which I just hacked together to
test something, it may contain eroRs but worked for my purpose.
Since the sine is the basis for all other waveforms its omission would
seem more than a little strange to anyone knowledgeable about audio, for
example.
regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
PyCon is coming! Atlanta, Feb 2010 http://us.pycon.org/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/
I don't know very much if anything about audio. For example, in the code what I
called "sawtooth" wave is really "triangle" wave. The sawtooth is simpler, what
I called "linear" in the code.
And if you wonder, I was just checking the terminology now before starting to
write it up... Perhaps should have done that before posting code. But once I got
the idea of posting it I just posted it.
Anyway, as I recall any wave can be decomposed into sine waves, or square waves,
or almost whatever kind of waves. Think about a square wave of frequency f and
one of frequency 3f and perhaps third the amplitude (not sure), combined that's
already a good start on a sine wave. With some ugly staircasing but hey. And as
a matter of programming practicality, a triangle wave sounds almost like a sine
wave. It's just a little more edgy or "hairy", a slight buzz.
Cheers,
- Alf
There is a very pretty discussion of all this, if you are mathematically
inclined, in
http://press.princeton.edu/books/maor/chapter_15.pdf
with a specific example for the sawtooth waveform.
I would definitely recommend renaming the waveforms you *do* use to
conform with accepted terminology. This will reduce reader confusion.
Oh. It's simple to prove. At least conceptually! :-)
Consider first that you need an infinite number of sine waves to create a
perfect square wave.
The opposite also holds: infinite number of square waves to create a perfect
sine wave (in a way sines and squares are opposites, the most incompatible).
With the goal of just a rough approximation you can go about it like this:
1. Divide a full cycle of the sine wave into n intervals. With
sine wave frequency f this corresponds to n*f sample rate for digital
representation.
2. Each interval will be approximated by a rectangular bar extending
up to or down to the sine wave. As it happens this (the bar's height) is
the sample value in a digital representation.
3. In the first half of the cycle, for each bar create that bar as
a square wave of frequency f, amplitude half the bar's height, and phase
starting at the bar's left, plus same square wave with negative sign
(inverted amplitude) and phase starting at the bar's right. And voil�,
not only this bar generated but also the corresponding other-way bar in
second half of cycle.
4. Sum all the square waves from step 3.
5. Let n go to infinity for utter perfectness! :-)
And likewise for any other waveform.
After all, it's the basis of digital representation of sound!
> though I admit the idea has
> intuitive appeal. But I know beyond a shadow of a doubt from my
> education that any periodic function with a fundamental frequency f can
> be approximated to whatever desired accuracy by the sum of sine and
> cosine waves of frequencies Nf (N = 0, 1, 2, 3, ...) of appropriate
> amplitudes (the frequency 0 component allows you to insert a "DC shift"
> for waveforms that aren't symmetrical about zero). I seem to remember
> the Fourier's theorem was the fundamental proof.
Yes, the good Fourier had a thing for roundish curves. He saw them everywhere
and in anything. Eventually he found an infinite number of them in, to others,
invisible /sound/.
> There is a very pretty discussion of all this, if you are mathematically
> inclined, in
>
> http://press.princeton.edu/books/maor/chapter_15.pdf
>
> with a specific example for the sawtooth waveform.
>
> I would definitely recommend renaming the waveforms you *do* use to
> conform with accepted terminology. This will reduce reader confusion.
Yes, thanks.
Cheers,
- Alf
> Just as a contribution, since someone hinted that I haven't really
> contributed much to the Python community.
>
> The [simple_sound] code will probably go into my ch 3 at <url:
> http://tinyurl.com/programmingbookP3>, but sans sine wave generation since
> I haven't yet discussed trig functions, and maybe /with/ changes suggested
> by you?
> def _append_as_big_endian_int16_to( a, i ):
> if i < 0:
> i = i + 65536
> assert( 0 <= i < 65536 )
> a.append( i // 256 )
> a.append( i % 256 )
> data = array.array( "B" ) # B -> unsigned bytes.
Do you know that array.array() supports 16 bit signed integers? There's even
a byteswap() method to deal with endianess.
> Utility class that may be used to capture output (an instance of this or
> any other file like class can be passed as "filename" to
> simple_sound.Writer):
>
> <code>
> class BytesCollector:
Are you reinventing io.BytesIO() here?
Peter
>> It's not clear to me that you can approximate any waveform with a
>> suitable combination of square waves,
>
> Oh. It's simple to prove. At least conceptually! :-)
>
> Consider first that you need an infinite number of sine waves to create a
> perfect square wave.
>
> The opposite also holds: infinite number of square waves to create a
> perfect sine wave (in a way sines and squares are opposites, the most
> incompatible).
No, it doesn't. The infinite set of sine waves that make a square wave
leave out the sine waves of frequency 2f, 4f, 6f, 8f, ... (2*n*f) ... .
Once you've left them out, you can never get them back. So sawtooth waves,
for example, can't generally be built out of sets of square waves.
You can inject even harmonics up to a given limit by adding "rectangular
waves" with given duty cycles, but the "given" part makes the math grubby.
Fourier transforms are cute to play with. If you don't care about run-time
there's:
#!/usr/bin/env python
# -*- coding: ASCII -*-
'''
$Id$'''
import math
def dft (sample):
'''Discrete Fourier Transform'''
n = len (sample)
n21 = n / 2 + 1
twopi = math.pi * 2.0
sin = math.sin
cos = math.cos
rex = [0]*n21
imx = [0]*n21
for k in xrange (n):
for i in xrange (n21):
a = twopi * k * i / n
rex[i] += sin(a) * sample[k]
imx[i] -= cos(a) * sample[k]
return rex, imx
#~ wave = [1]*32 + [-1]*32 # rectangular duty-cycle 1/2
#~ wave = [3]*16 + [-1]*48 # rectangular duty-cycle 1/4
wave = [7]*8 + [-1]*56 # rectangular duty-cycle 1/8
#~ wave = [15]*4 + [-1]*60 # rectangular duty-cycle 1/16
#~ wave = [31]*2 + [-1]*62 # rectangular duty-cycle 1/32
rex, imx = dft (wave)
print rex
print
print imx
The real coefficients show how the 8th, 16th, 24th, 32nd harmonics -- where
the coefficients are near zero -- have dropped out of the waveform.
Mel.
The way to build a sine wave out of square waves is not a Fourier transform.
See the post you replied to for a simple procedure to build the sine wave.
Cheers & hth.,
- Alf
Bullshit.
Cheers,
- Alf
Yes. I used bytes since that seemed to be what [wave] required. But I tested now
and at least [aifc] handles 16-bit integers fine.
Hm, nice example down the drain...
> There's even
> a byteswap() method to deal with endianess.
>
>> Utility class that may be used to capture output (an instance of this or
>> any other file like class can be passed as "filename" to
>> simple_sound.Writer):
>>
>> <code>
>> class BytesCollector:
>
> Are you reinventing io.BytesIO() here?
Probably. :-) Checking... Yes, I was; thanks!
Cheers,
- Alf
>> It's not clear to me that you can approximate any waveform
>> with a suitable combination of square waves,
>
> Oh. It's simple to prove. At least conceptually! :-)
[...]
> With the goal of just a rough approximation you can go about
> it like this:
>
> 1. Divide a full cycle of the sine wave into n intervals.
> With sine wave frequency f this corresponds to n*f
> sample rate for digital representation.
>
> 2. Each interval will be approximated by a rectangular bar
> extending up to or down to the sine wave. As it happens
> this (the bar's height) is the sample value in a digital
> representation.
>
> 3. In the first half of the cycle, for each bar create that
> bar as a square wave of frequency f, amplitude half the
> bar's height, and phase starting at the bar's left, plus
> same square wave with negative sign (inverted amplitude)
> and phase starting at the bar's right. And voil?, not
> only this bar generated but also the corresponding
> other-way bar in second half of cycle.
>
> 4. Sum all the square waves from step 3.
>
> 5. Let n go to infinity for utter perfectness! :-)
>
> And likewise for any other waveform.
>
> After all, it's the basis of digital representation of sound!
Huh? I've only studied basic DSP, but I've never heard/seen
that as the basis of digital represention of sound. I've also
never seen that representation used anywhere. Can you provide
any references?
--
Grant Edwards grante Yow! CHUBBY CHECKER just
at had a CHICKEN SANDWICH in
visi.com downtown DULUTH!
Oh, you have... The end result above (for finite n) is a sequence of sample
values of a sine wave. Ordinary digital representation of sound is exactly the
same, a sequence of sample values.
> I've also never seen that representation used anywhere.
Yes, you have. A sequence of sample values is the representation used in any
direct wave file. Like [.wav] and, I believe, [.aiff].
> Can you provide any references?
I don't have any references for the above procedure, it's sort of trivial.
Probably could find some references with an hour of googling. But no point.
Cheers & hth.,
- Alf
PS: To extend the above to a non-symmetric waveform, just first decompose that
waveform into sine waves (Fourier transform), then add up the square wave
representations of each sine wave. :-)
But don't listen to me, apparently I'm full of it.
Just for the record: Grant has seen that representation numerous times, he just
didn't recognize it.
> Can you provide any references?
>>
> Of course he can't. And it isn't the basis of analog quantization.
Of course it isn't the basis of quantization: it *is* quantization, directly.
Which is the basis of digital representation.
> And I suspect Alf has never hear of Shannon's theorem.
It's about 30 years since I did that stuff.
> But don't listen to me, apparently I'm full of it.
You're spouting innuendo and personal attacks, repeatedly, so that seems to be
the case, yes. :-)
Cheers,
- Alf
> Which is the basis of digital representation.
>
Well at least we agree that quantization is the basis of digital
representations. But I've never seen any summed square wave presentation
of it.
>
>> And I suspect Alf has never hear of Shannon's theorem.
>
> It's about 30 years since I did that stuff.
>
Well me too, but information theory is, after all, the theoretical
underpinning for the way I make my living, so I felt obliged to study it
fairly thoroughly.
>
>> But don't listen to me, apparently I'm full of it.
>
> You're spouting innuendo and personal attacks, repeatedly, so that seems
> to be the case, yes. :-)
>
Nothing personal about it. I'm just asking you to corroborate statements
you have made which, in my ignorance, I consider to be bogus hand-waving.
Nothing about you there. Just the information you are promoting. I don't
normally deal in innuendo and personal attacks. Though I do occasionally
get irritated by what I perceive to be hogwash. People who know me will
tell you, if I am wrong I will happily admit it.
There are two three things wrong with that paragraph. First, quantization can
not physically be instantaneous. So it's pretty much moot to try to restrict it
to that.
Second, for the mathematical exercise you can let the measurement be an
instantaneous value, that is, the value at a single point.
Third, the result of quantization is a sequence of values, each value present
for an interval of time, and the same is obtained by summing square waves.
I'm beginning to believe that you maybe didn't grok that simple procedure.
It's very very very trivial, so maybe you were looking for something more
intricate -- they used to say, in the old days, "hold on, this proof goes by
so fast you may not notice it!"
>> Which is the basis of digital representation.
>>
> Well at least we agree that quantization is the basis of digital
> representations. But I've never seen any summed square wave presentation
> of it.
Well, I'm glad to be able to teach something.
Here's the MAIN IDEA in the earlier procedure: a square wave plus same wave
offset (phase) and negated, produces a set of more rectangular waveforms, which
I called "bars" -- in between the bars the two square waves cancel each other:
_
| |
| |
______| |______ ______
| |
| |
|_|
The bars alternate in positive and negative direction. They can be made as
narrow as you wish, and when they are as narrow as each sample interval then
each bar can represent the sample value residing in that interval. Even a sample
corresponding to a point measurement (if such were physically possible).
And samples are all that's required for representing the sine. And happily the
alternation of upgoing and downgoing bars is matched by an identical alternation
of the sine wave. :-) Otherwise it would be a tad difficult to do this.
But then, representing sines is all that's required for representing any wave
form, since any wave form can be decomposed into sines.
>>> And I suspect Alf has never hear of Shannon's theorem.
>> It's about 30 years since I did that stuff.
>>
> Well me too, but information theory is, after all, the theoretical
> underpinning for the way I make my living, so I felt obliged to study it
> fairly thoroughly.
>>> But don't listen to me, apparently I'm full of it.
>> You're spouting innuendo and personal attacks, repeatedly, so that seems
>> to be the case, yes. :-)
>>
> Nothing personal about it. I'm just asking you to corroborate statements
> you have made which, in my ignorance, I consider to be bogus hand-waving.
OK.
May I then suggest going through the procedure I presented and *draw* the square
waves.
I dunno, but maybe that can help.
> Nothing about you there. Just the information you are promoting. I don't
> normally deal in innuendo and personal attacks. Though I do occasionally
> get irritated by what I perceive to be hogwash. People who know me will
> tell you, if I am wrong I will happily admit it.
There's a difference between an algorithm that you can implement, and hogwash.
Cheers,
- Alf
Since you said it's trivial, then...
>> Nothing about you there. Just the information you are promoting. I don't
>> normally deal in innuendo and personal attacks. Though I do occasionally
>> get irritated by what I perceive to be hogwash. People who know me will
>> tell you, if I am wrong I will happily admit it.
>
> There's a difference between an algorithm that you can implement, and
> hogwash.
please prove your claim by writing that algorithm in code and post it in
this list. The program must accept a .wav file (or sound format of your
choice) and process it according to your algorithm and the output
another .wav file (or sound format of your choice) that sounds roughly
similar to the input file.
PS: I have near-zero experience with sound processing
PPS: I will be equally delighted seeing either Steve admitting his wrong
or you admit your hogwash
PPPS: An alternative way to convince us is to provide a paper/article
that describes this algorithm.
PPPPS: Though I will be quite sad if you choose to reject the challenge
You can't get it more trivial.
>>> Nothing about you there. Just the information you are promoting. I don't
>>> normally deal in innuendo and personal attacks. Though I do occasionally
>>> get irritated by what I perceive to be hogwash. People who know me will
>>> tell you, if I am wrong I will happily admit it.
>> There's a difference between an algorithm that you can implement, and
>> hogwash.
>
> please prove your claim by writing that algorithm in code and post it in
> this list. The program must accept a .wav file (or sound format of your
> choice) and process it according to your algorithm and the output
> another .wav file (or sound format of your choice) that sounds roughly
> similar to the input file.
First, the (very very trivial) algorithm I posted does *not* do that: by itself
it represents a sine wave, not an arbitrary wave form.
And second I'm not about to write Fourier transform code to satisfy someone who
refuses to do a milligram of thinking.
The Fourier part stuff needed to do what you're requesting is non-trivial, or at
least it's some work to write the code.
> PS: I have near-zero experience with sound processing
> PPS: I will be equally delighted seeing either Steve admitting his wrong
> or you admit your hogwash
> PPPS: An alternative way to convince us is to provide a paper/article
> that describes this algorithm.
> PPPPS: Though I will be quite sad if you choose to reject the challenge
I don't believe any of what you write here.
In addition to my earlier reply (that the presented algorithm itself doesn't
generate arbitrary wave forms but only sines, i.e. that you're totally off the
mark),
your request is meaningless in another way, namely:
the algorithm does synthesis, which is easy (like computer speech), while you're
asking for analysis (like computer speech recognition), which is hard.
But maybe you're simply not able to understand the algorithm, trivial as it is.
So, a Python implementation (note, this program takes some time to run!):
<code filename="sinewave_from_squares.py">
# Generating a sine wave as a sum of square waves of various amplitudes & phases.
import simple_sound
# Step 1 "Divide a full cycle of the sine wave into n intervals."
n = 100
# Step 2 -- Just an explanation of the rest
# Step 3 "In the first half of the cycle, for each bar create that bar as
# a square wave of frequency f, amplitude half the bar's height, and phase
# starting at the bar's left, plus same square wave with negative sign
# (inverted amplitude) and phase starting at the bar's right."
square_waves = []
for i in range( n//2 ):
middle_of_interval = (i + 0.5)/n
amp = simple_sound.sample_sine( 1, middle_of_interval ) / 2
def first_square_wave( t, i = i, amp = amp ):
phase = i/n
return amp*simple_sound.sample_square( 1, t - phase )
def second_square_wave( t, i = i, amp = amp ):
phase = (i + 1)/n
return -amp*simple_sound.sample_square( 1, t - phase )
square_waves.append( first_square_wave )
square_waves.append( second_square_wave )
# Step 4 "Sum all the square waves from step 3."
def sample_squares( f, t ):
samples = []
o_time = f*t
for func in square_waves:
sq_sample = func( o_time )
samples.append( sq_sample )
return sum( samples )
# Finally, generate this is in a [.aiff] file so as to possibly satisfy Lie Rian:
if True:
f = 440
sample_rate = 44100
total_time = 2
n_samples = sample_rate*total_time
writer = simple_sound.Writer( "sinewave.aiff" )
for i in range( n_samples ):
t = 1*i/sample_rate
sample = sample_squares( f, t )
writer.write( sample )
writer.close()
</code>
I will, however, observe that your definition of a square wave is what I
would have to call a "'square' wave" (and would prefer to call a "pulse
train"), as I envisage a square wave as a waveform having a 50% duty
cycle, as in
___ ___
| | | |
| | | |
| | | |
+---+---+---+---+ and so on ad infinitum, (though I might allow you
| | | | to adjust the position
| | | | of y=0 if you want)
|___| |___|
as opposed to your
_
| |
| |
______| |______ ______
| |
| |
|_|
So I can see how we might be at cross purposes. I could cite authorities
for differentiating between a square wave and a symmetric pulse train,
but will content myself with observing only that my impression is the
common definition of an ideal square wave (one with a zero transition
time) admits of only two instantaneous values, eschewing the zero you
use. If that is the case, we could perhaps agree that we differ merely
on terminology.
Or, best of all, you could show me how to synthesize any waveform by
adding square waves with a 50% duty cycle. Then I *will* be impressed.
not-wriggling-real-ly y'rs - steve
You're again into innuendo, misleading statements and so forth. Lie Ryan's
challenge is nothing but trivial, because it's about implementing very much more
than the algorithm. I did implement the algorithm for him, in Python, and posted
that in this thread.
> I will, however, observe that your definition of a square wave is what I
> would have to call a "'square' wave" (and would prefer to call a "pulse
> train"), as I envisage a square wave as a waveform having a 50% duty
> cycle, as in
>
> ___ ___
> | | | |
> | | | |
> | | | |
> +---+---+---+---+ and so on ad infinitum, (though I might allow you
> | | | | to adjust the position
> | | | | of y=0 if you want)
> |___| |___|
>
> as opposed to your
>
> _
> | |
> | |
> ______| |______ ______
> | |
> | |
> |_|
>
Try to read again, a sufficient number of times, how to generate the latter by
summing *two instances of the former*.
I'm sorry to say this but I find it hard to explain things simple enough for
you, because at the level of 2+2 any explanation is far more complex than the
concept itself.
That is, of course, a challenge to me! :-)
So, thanks for challenging my pedagogical abilities.
I know they're not as good as they should be, and need some exercise!
> So I can see how we might be at cross purposes. I could cite authorities
> for differentiating between a square wave and a symmetric pulse train,
> but will content myself with observing only that my impression is the
> common definition of an ideal square wave (one with a zero transition
> time) admits of only two instantaneous values, eschewing the zero you
> use. If that is the case, we could perhaps agree that we differ merely
> on terminology.
No, we don't differ on terminology: we seem to differ in that one of us has
severe difficulties understanding the very simplest things, such as what graph
one gets by summing two squares waves of same frequency but out of phase.
The one of us who has trouble understanding that is also apparently too lazy to
try out the earlier advice given him of graphing this on a piece of paper, and
instead prefers to spout innuendu, personal attacks and misleading statements.
That's a real challenge to the other person.
> Or, best of all, you could show me how to synthesize any waveform by
> adding square waves with a 50% duty cycle. Then I *will* be impressed.
You would, yes?
Perhaps you'd also admit to being wrong, and retract your innuoendo etc.?
Well, just epress the waveform as a sum of sine waves (Fourier transform);
synthesize each sine wave by a set of 50% duty cycle square waves (I've earlier
posted Python code for that in this thread); add all the square waves. The hard
part of this is implementing the Fourier transform, I leave that to you. ;-)
Cheers & htfh.,
- Alf
regards
Steve
regards
Steve
regards
Steve
OK.
> Feel free to remind me what that was.
It would IMHO serve no good purpose to reiterate that. At this point, let's just
start fresh.
> You're again into innuendo, misleading statements and so forth.
[...]
> [Steve Holden] prefers to spout innuendu, personal attacks and
> misleading statements.
Your constant and repeated accusations that any questioning of you is
deliberate lying and innuendo (innuendo of what, I wonder?) is getting
tiresome. You did this to me, accusing me of deliberate lying, and now
you're doing the same to Steve.
Just because people question the (alleged) facts as you report it doesn't
mean they're deliberately trying to mislead others. Hasn't it occurred to
you that perhaps we're not deliberately lying to make you look bad but
actually think you are mistaken? A difference of opinion is not evidence
that people are lying.
For somebody who fancies himself a writer teaching beginners to
programming, your communication skills leave something to be desired. I
hope your book is better than your posts, because from what I have seen
your writing in this newsgroup is frequently sloppy and hard to
understand, you tend to use terms in non-standard ways and then take any
misunderstandings as a personal affront, and if anyone questions what you
say, you're likely bristle and start looking for a fight.
I don't know what your problem is, but I wish you'd deal with it in
private and stop dumping it on the rest of us. You're a smart fellow who
brings much of value to the community, but dealing with your posts is
becoming more trouble than they're worth.
--
Steven
You did lie, that's established. In addition as I recall in the same post you
went on about my motivations for doing the Terrible Deed that you invented. And
you named one poor person who'd made a silly mistake, stating that he didn't do
what the logs clearly showed he did do, and I think that was to force me to
argue a case against him (but happily he did a superb job after that initial
mistake, it would have been much harder for me to respond to your egging if not
for that very good job he did).
And now you're trying to get me to argue a case against another person.
That's not very nice.
> Just because people question the (alleged) facts as you report it doesn't
> mean they're deliberately trying to mislead others. Hasn't it occurred to
> you that perhaps we're not deliberately lying to make you look bad but
> actually think you are mistaken? A difference of opinion is not evidence
> that people are lying.
Mostly I assume that people who disagree with me think I'm mistaken, yes.
But when someone writes an article that's mainly or only about me, as has
happened once earlier in this thread and that now you're doing, then I of course
assume that it's personal, that the person writing has transferred some
grievances from the technical domain to the personal domain.
> For somebody who fancies himself a writer teaching beginners to
> programming, your communication skills leave something to be desired. I
> hope your book is better than your posts, because from what I have seen
> your writing in this newsgroup is frequently sloppy and hard to
> understand, you tend to use terms in non-standard ways and then take any
> misunderstandings as a personal affront, and if anyone questions what you
> say, you're likely bristle and start looking for a fight.
I'm probably a bad person, he he. :-)
> I don't know what your problem is, but I wish you'd deal with it in
> private and stop dumping it on the rest of us. You're a smart fellow who
> brings much of value to the community, but dealing with your posts is
> becoming more trouble than they're worth.
Well please stop posting these silly articles about me, and *please* stop
dragging other people into it.
> You did lie, that's established. In addition as I recall in the same
> post you went on about my motivations for doing the Terrible Deed that
> you invented.
None of that matches my (largely disinterested) observations. This is
pure fantasy, as best I can tell.
I've tried in private to talk you out of this persecution fantasy;
others have tried in public. It all seems to be in vain.
You've now passed my twit threshold, so welcome to my kill file.
*plonk*
--
\ “Pinky, are you pondering what I'm pondering?” “Umm, I think |
`\ so, Brain, but three men in a tub? Ooh, that's unsanitary!” |
_o__) —_Pinky and The Brain_ |
Ben Finney
It's decidedly not kind of you to try to re-open those issues.
Anybody can have a bad day and write something that's untrue or actively
misleading, whatever.
When it's been dealt with, and it has, it should not be reiterated.
> I've tried in private to talk you out of this persecution fantasy;
> others have tried in public. It all seems to be in vain.
It's quite normal for people to use ad hominem arguments, especially when they
have chosen an indefensible position; that's why the technique has a name.
There's no persecution involved in that.
And people are not necessarily bad even when they do that from time to time, as
you do here: it's human.
> You've now passed my twit threshold, so welcome to my kill file.
Goodbye.
How you can possibly hope that helps anyone except you I can't possibly
imagine. It seems that any request to question your own behavior, any
remark about how it might usefully change, is taken to be an ad homime,
attack.
As my wife (who has a colorful Scottish turn of phrase) might say, if
you were chocolate you would eat yourself.
Please, get over this obsession with being "right".
regards
Steve
You have (so far) refused to outright admit that you were wrong here, going to
the extreme half-way measure of using the word "wring" or something, perhaps
hoping that I'd interpret it one way and most everyone else another way.
I do respond to articles which are technically wrong, especially follow-ups to
my own articles, as your (at least initial) POV was in this thread: wrong.
That's not an obsession, nor is it a desire to be right, it's the *usual* Usenet
culture: one is expected to respond and to correct technical issues, and to not
collect social points at the expense of technical correctness.
And since it's you who brings this up again, and since earlier you wrote ...
"I herebe retract anything I have said about you that you consider
innuendo. Feel free to remind me what that was."
... I now feel free to remind you about some of it.
Instead of saying OK or thanks or whatever normal for a simple explanation, you
pretended that my explanation was some kind of thesis from me and "this is
merely hand-waving. It looks appealing, but there's no rigor there".
And with our last discussion before this one fresh in mind I told you that that
was bullshit, using just that single word. But to expand on that: the
insinuation that the explanation was some kind of thesis from me was bullshit,
that it was merely "hand-waiving" was bullshit (while informal it was an exact
algorithm, and later in this thread I posted Python code implementing it), and
that it had "no rigor" was bullshit since it was an exact algorithm; moreover it
was a *trivial* algorithm, and as demonstrated, it works.
In short, the insinuation that I was some kind of crank posting a thesis that
lacked "rigor" and was "hand-waiving" was utter bullshit: it was a trivial and
exact algorithm, an explanation in response to your own question, and it
demonstrably works.
In response to someone else you then posted this:
<quote>
Grant Edwards wrote:
> > On 2010-01-14, Alf P. Steinbach <al...@start.no> wrote:
[bogus hand-waving]
>> >> After all, it's the basis of digital representation of sound!
> >
> > Huh? I've only studied basic DSP, but I've never heard/seen
> > that as the basis of digital represention of sound. I've also
> > never seen that representation used anywhere. Can you provide
> > any references?
> >
Of course he can't. And it isn't the basis of analog quantization. And I
suspect Alf has never hear of Shannon's theorem.
But don't listen to me, apparently I'm full of it.
regards
Steve
</quote>
* The "of course he can't [provide a reference]"
is the same insinuation you made earlier repeated, that what I posted was
drivel, when it was a trivial and exact algorithm, one that does work.
* "Isn't the basis of analog quantization"
makes the reader believe I'd written that nonsense "basis of analog
quantization". I did not write such nonsense but readers get the impression that
I did. The quote above shows what I wrote instead of the words you put in my mouth.
* "And I suspect Alf has never hear of Shannon's theorem."
is solely about me, an insinuation about my education or competence.
* "But don't listen to me, apparently I'm full of it."
gives the reader the impression that I've written earlier that you're "full of it".
I haven't ever written that.
And /nothing/ in that posting was about any technical issue, each and every of
the four sentences just an ad hominem attack.
I very seldom do ad hominem (it has happened, yes, I'm human too). But this is
the second or third time you've responded to me with that last sentence, and it
implies that I've written that about you, that I'd earlier done unto you what
you were doing to me. I haven't, although in my follow-up, annoyed by all this
stuff from you, I then agreed with your self-evaluation, for which I apologize.
Pwned! Good one Alf :)
I think those guys owe you an apology really, but I wouldn't hold your
breath!
Roger.
For the record, yes, summing any waveforms that can be represented as
Fourier Series will necessarily result in another Fourier series, since
any linear combination of Fourier series must itself, be a Fourier
series, and therefore the representation of the sum of the summed waveforms.
And, by the way, "wring" was a genuine typo, though I admit I chose at
the proof reading stage to leave it in for (as I thought) humorous
effect. If I'm the only person who saw that as the slightest bit amusing
then again, I am sorry.
> I do respond to articles which are technically wrong, especially
> follow-ups to my own articles, as your (at least initial) POV was in
> this thread: wrong.
>
Wrong, wrong, wrong.
> That's not an obsession, nor is it a desire to be right, it's the
> *usual* Usenet culture: one is expected to respond and to correct
> technical issues, and to not collect social points at the expense of
> technical correctness.
>
But not to characterize honest disagreements in such discussions as ad
hominem attacks.
> And since it's you who brings this up again, and since earlier you wrote
> ...
>
> "I herebe retract anything I have said about you that you consider
> innuendo. Feel free to remind me what that was."
>
> ... I now feel free to remind you about some of it.
>
> Instead of saying OK or thanks or whatever normal for a simple
> explanation, you pretended that my explanation was some kind of thesis
> from me and "this is merely hand-waving. It looks appealing, but there's
> no rigor there".
>
I did. You provided an explanation I did not understand, and I
arrogantly assumed I had something to teach you about the subject. You
have shown me the error of my ways, and I am grateful for it.
> And with our last discussion before this one fresh in mind I told you
> that that was bullshit, using just that single word. But to expand on
> that: the insinuation that the explanation was some kind of thesis from
> me was bullshit, that it was merely "hand-waiving" was bullshit (while
> informal it was an exact algorithm, and later in this thread I posted
> Python code implementing it), and that it had "no rigor" was bullshit
> since it was an exact algorithm; moreover it was a *trivial* algorithm,
> and as demonstrated, it works.
>
I haven't listened to any resulting wav files yet, but yes, you posted code.
> In short, the insinuation that I was some kind of crank posting a thesis
> that lacked "rigor" and was "hand-waiving" was utter bullshit: it was a
> trivial and exact algorithm, an explanation in response to your own
> question, and it demonstrably works.
>
> In response to someone else you then posted this:
>
>
> <quote>
> Grant Edwards wrote:
>> > On 2010-01-14, Alf P. Steinbach <al...@start.no> wrote:
> [bogus hand-waving]
>>> >> After all, it's the basis of digital representation of sound!
>> >
>> > Huh? I've only studied basic DSP, but I've never heard/seen
>> > that as the basis of digital represention of sound. I've also
>> > never seen that representation used anywhere. Can you provide
>> > any references?
>> >
> Of course he can't. And it isn't the basis of analog quantization. And I
> suspect Alf has never hear of Shannon's theorem.
>
> But don't listen to me, apparently I'm full of it.
>
> regards
> Steve
> </quote>
>
>
> * The "of course he can't [provide a reference]"
>
> is the same insinuation you made earlier repeated, that what I posted
> was drivel, when it was a trivial and exact algorithm, one that does work.
>
I suspect there was a glitch in mailing list/newsgroup synchronization
there. When I wrote those remarks you may have *posted* your code, but I
know I had not read it.
>
> * "Isn't the basis of analog quantization"
>
> makes the reader believe I'd written that nonsense "basis of analog
> quantization". I did not write such nonsense but readers get the
> impression that I did. The quote above shows what I wrote instead of the
> words you put in my mouth.
>
>
> * "And I suspect Alf has never hear of Shannon's theorem."
>
> is solely about me, an insinuation about my education or competence.
>
>
> * "But don't listen to me, apparently I'm full of it."
>
> gives the reader the impression that I've written earlier that you're
> "full of it".
>
> I haven't ever written that.
>
When you call something I write bullshit, since it came out of me, I am
merely exaggerating amount the amount of shit you appear to think I have
in me. The fact that now see it *was* bullshit, of course, does give
your words a certain force I have not previously allowed them.
> And /nothing/ in that posting was about any technical issue, each and
> every of the four sentences just an ad hominem attack.
>
> I very seldom do ad hominem (it has happened, yes, I'm human too). But
> this is the second or third time you've responded to me with that last
> sentence, and it implies that I've written that about you, that I'd
> earlier done unto you what you were doing to me. I haven't, although in
> my follow-up, annoyed by all this stuff from you, I then agreed with
> your self-evaluation, for which I apologize.
>
> Cheers & hth.,
>
> - Alf
Don't worry about it. Fortunately it isn't the first time I have been
wrong in public, and it probably won't be the last. Sorry for the
offensive tone of some of my remarks.
> I will, however, observe that your definition of a square wave is what I
> would have to call a "'square' wave" (and would prefer to call a "pulse
> train"), as I envisage a square wave as a waveform having a 50% duty
> cycle, as in
>
> ___ ___
> | | | |
> | | | |
> | | | |
> +---+---+---+---+ and so on ad infinitum, (though I might allow you
> | | | | to adjust the position
> | | | | of y=0 if you want)
> |___| |___|
That is a square wave.
> as opposed to your
>
> _
> | |
> | |
> ______| |______ ______
> | |
> | |
> |_|
That isn't.
Arguing to the contrary is just being Humpty Dumpty...
> Or, best of all, you could show me how to synthesize any
> waveform by adding square waves with a 50% duty cycle. Then I
> *will* be impressed.
Isn't that what he claimed? He said that his algorithm for
summing square waves demonstrated the converse of the ability
to construct a periodic function (like a square wave) from a
sine-cosine summation.
--
Grant Edwards grante Yow! On the road, ZIPPY
at is a pinhead without a
visi.com purpose, but never without
a POINT.
I stand corrected, fair play sir :)
Roger.
As it is I do not know whether the above represents what I've written, or might
perhaps be /misprepresenting/ the conclusions of this thread.
If I knew a lot more about Fourier series (it's been a long time since college!
lots forgotten) I might agree or disagree with the above as it applies to
constructing a sine wave from square waves.
I just note that representing a sine wave as square waves, forming it from
square waves, in the practical sense works (demonstrated here), even though it's
totally impractical :-), but just in answer to your original question.
And I note that in the mathematematical sense when n goes to infinity and
vanishingly thin pulses result from sums of square waves, like *impulse* waves,
then one is over in some regime where it is not at all clear to me that it is
valid to talk about Fourier series any more. Perhaps it's valid if the term
"Fourier series" does not necessarily imply sum of sine waves. I don't know.
I'm guessing that applying the Fourier series view for that is like actually
dividing by zero to maintain the running product of a collection of numbers when
an instance of 0 is removed from the collection.
It's no practical problem to maintain a running product (for programmers it's
interesting to note that polar representation complex numbers can do the job),
and it's well-defined also mathematically, with any /reasonable/ approach. But
the simple-minded direct way, just dividing the current product by the number
removed from the collection, is then invalid. And some people might take that
limited applicability of the direct simple way as evidence that it's impossible
to remove numbers, failing to see any of the trivial practical solutions. ;-)
Cheers,
- Alf
Neither I nor Steve has called that latter wave a square wave.
Steve, quoted above, has written that I defined a square wave that way. I have
not. So Steve's statement is a misrepresentation (I described it as a sum of two
square waves, which it is), whatever the reason for that misrepresentation.
>> Or, best of all, you could show me how to synthesize any
>> waveform by adding square waves with a 50% duty cycle. Then I
>> *will* be impressed.
>
> Isn't that what he claimed? He said that his algorithm for
> summing square waves demonstrated the converse of the ability
> to construct a periodic function (like a square wave) from a
> sine-cosine summation.
Not by itself, no: it just synthesizes a sine.
For the more general case read e.g. the PS in my reply to your earlier (single)
article in this thread.
For information about what the algorithm does, what you refer to as a "claim"
(but note that a Python implementation has been posted to this thread, and that
it works, and that besides the algorithm is trivial so that "claim" is a rather
meaningless word here), read the article that you then responded to.
$ od -bc sinewave.aiff
0000000 106 117 122 115 000 002 261 076 101 111 106 106 103 117 115 115
F O R M \0 002 261 > A I F F C O M M
0000020 000 000 000 022 000 001 000 001 130 210 000 020 100 016 254 104
\0 \0 \0 022 \0 001 \0 001 X 210 \0 020 @ 016 254 D
0000040 000 000 000 000 000 000 123 123 116 104 000 002 261 030 000 000
\0 \0 \0 \0 \0 \0 S S N D \0 002 261 030 \0 \0
0000060 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
\0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0530500 000 000 000 000 000 000
\0 \0 \0 \0 \0 \0
0530506
Any idea what I did wrong?
regards
Steve
That sounds like something I did wrong, not like something you did wrong:
It sounds like a ... BUG! ... in my simple_writer code. :-)
Or, that's perhaps not funny, but it occurred to me that it might, to some at
least, appear to be sort of incongruous in the context of the earlier thread. Heh.
Checking first 20 sample values generated:
<code>
if True:
f = 440
sample_rate = 44100
total_time = 2
n_samples = sample_rate*total_time
writer = simple_sound.Writer( "sinewave.aiff" )
for i in range( n_samples ):
t = 1*i/sample_rate
sample = sample_squares( f, t )
if i < 20: print( sample ) # Check 'em
writer.write( sample )
writer.close()
</code>
<output>
-0.0314107590781
-0.0314107590781
-0.0941083133185
-0.15643446504
-0.218143241397
-0.278991106039
-0.338737920245
-0.397147890635
-0.45399049974
-0.50904141575
-0.562083377852
-0.612907053653
-0.661311865324
-0.707106781187
-0.75011106963
-0.790155012376
-0.827080574275
-0.860742027004
-0.891006524188
-0.917754625684
</output>
Checking generated file:
<dump>
$ od -bc sinewave.aiff | head
0000000 106 117 122 115 000 001 130 266 101 111 106 106 103 117 115 115
F O R M \0 001 X 266 A I F F C O M M
0000020 000 000 000 022 000 001 000 000 254 104 000 020 100 016 254 104
\0 \0 \0 022 \0 001 \0 \0 254 D \0 020 @ 016 254 D
0000040 000 000 000 000 000 000 123 123 116 104 000 001 130 220 000 000
\0 \0 \0 \0 \0 \0 S S N D \0 001 X 220 \0 \0
0000060 000 000 000 000 000 000 373 373 373 373 363 364 353 372 344 024
\0 \0 \0 \0 \0 \0 373 373 373 373 363 364 353 372 344 024
0000100 334 112 324 245 315 053 305 344 276 330 270 016 261 215 253 133
334 J 324 245 315 + 305 344 276 330 270 016 261 215 253 [
</dump>
Hm, I'm inclined to think that you used Python 2.x instead of my 3.1.1!
I no longer have Python 2.x installed, I think, so no time to test that now.
But would that be the case?
If so, perhaps changing "t = 1*i/sample_rate" to "t = (1.0*i)/sample_rate" will
help?
Cheers,
- Alf
> Hm, I'm inclined to think that you used Python 2.x instead of my 3.1.1!
>
> I no longer have Python 2.x installed, I think, so no time to test that
> now.
>
> But would that be the case?
>
> If so, perhaps changing "t = 1*i/sample_rate" to "t =
> (1.0*i)/sample_rate" will help?
I fixed up two more divisions. Not sure if this is 2.x-compatible, but perhaps:
<code>
# Python 3.1.1 -- *probably* works also with 2.x?
# Generating a sine wave as a sum of square waves of various amplitudes & phases.
import simple_sound
# Step 1 "Divide a full cycle of the sine wave into n intervals."
n = 100
# Step 2 -- Just an explanation of the rest
# Step 3 "In the first half of the cycle, for each bar create that bar as
# a square wave of frequency f, amplitude half the bar's height, and phase
# starting at the bar's left, plus same square wave with negative sign
# (inverted amplitude) and phase starting at the bar's right."
square_waves = []
for i in range( n//2 ):
middle_of_interval = (i + 0.5)/n
amp = simple_sound.sample_sine( 1, middle_of_interval ) / 2.0
def first_square_wave( t, i = i, amp = amp ):
phase = 1.0*i/n
return amp*simple_sound.sample_square( 1.0, t - phase )
def second_square_wave( t, i = i, amp = amp ):
phase = 1.0*(i + 1)/n
return -amp*simple_sound.sample_square( 1.0, t - phase )
square_waves.append( first_square_wave )
square_waves.append( second_square_wave )
# Step 4 "Sum all the square waves from step 3."
def sample_squares( f, t ):
samples = []
o_time = f*t
for func in square_waves:
sq_sample = func( o_time )
samples.append( sq_sample )
return sum( samples )
# Finally, generate this is in an [.aiff] file:
if True:
f = 440
sample_rate = 44100
total_time = 2
n_samples = sample_rate*total_time
writer = simple_sound.Writer( "sinewave.aiff" )
for i in range( n_samples ):
t = 1.0*i/sample_rate
sample = sample_squares( f, t )
writer.write( sample )
writer.close()
</code>
That was indeed the case. So here you have an interesting example of a
piece of code that is pathological in Python2. All you have to change is
to add
from __future__ import __division__
and bingo! It's a multi-language program. But try seeing what 2to3 says
about your Python3 code :)
I will forgive you the omission of the ".0" because I too would assume
that it would be slower. Of course another technique is to scale the
already floating-point raw values before dividing by the range of the
analog output - then an integer division should work basically fine?
Under Python 3 I see
0000000 106 117 122 115 000 002 261 076 101 111 106 106 103 117 115 115
F O R M \0 002 261 > A I F F C O M M
0000020 000 000 000 022 000 001 000 001 130 210 000 020 100 016 254 104
\0 \0 \0 022 \0 001 \0 001 X 210 \0 020 @ 016 254 D
0000040 000 000 000 000 000 000 123 123 116 104 000 002 261 030 000 000
\0 \0 \0 \0 \0 \0 S S N D \0 002 261 030 \0 \0
0000060 000 000 000 000 000 000 373 373 373 373 363 364 353 372 344 024
\0 \0 \0 \0 \0 \0 373 373 373 373 363 364 353 372 344 024
0000100 334 112 324 245 315 053 305 344 276 330 270 016 261 215 253 133
334 J 324 245 315 + 305 344 276 330 270 016 261 215 253 [
0000120 245 176 237 375 232 335 226 043 221 324 215 364 212 210 207 222
245 ~ 237 375 232 335 226 # 221 324 215 364 212 210 207 222
0000140 205 026 203 026 201 224 200 222 200 021 200 021 200 222 201 224
205 026 203 026 201 224 200 222 200 021 200 021 200 222 201 224
0000160 203 026 205 026 207 222 212 210 215 364 221 324 226 043 232 335
203 026 205 026 207 222 212 210 215 364 221 324 226 # 232 335
0000200 237 375 245 176 253 133 261 215 270 016 276 330 305 344 315 053
237 375 245 ~ 253 [ 261 215 270 016 276 330 305 344 315 +
0000220 324 245 334 112 344 024 353 372 363 364 373 373 004 005 014 014
324 245 334 J 344 024 353 372 363 364 373 373 004 005 \f \f
and so on, but I still get silence from the Quicktime player.
That was indeed the case. So here you have an interesting example of a
piece of code that is pathological in Python2. All you have to change is
to add
from __future__ import __division__
and bingo! It's a multi-language program. But try seeing what 2to3 says
about your Python3 code :)
I will forgive you the omission of the ".0" because I too would assume
that it would be slower. Of course another technique is to scale the
already floating-point raw values before dividing by the range of the
analog output - then an integer division should work basically fine?
Under Python 3 I see
0000000 106 117 122 115 000 002 261 076 101 111 106 106 103 117 115 115
F O R M \0 002 261 > A I F F C O M M
0000020 000 000 000 022 000 001 000 001 130 210 000 020 100 016 254 104
\0 \0 \0 022 \0 001 \0 001 X 210 \0 020 @ 016 254 D
0000040 000 000 000 000 000 000 123 123 116 104 000 002 261 030 000 000
\0 \0 \0 \0 \0 \0 S S N D \0 002 261 030 \0 \0
0000060 000 000 000 000 000 000 373 373 373 373 363 364 353 372 344 024
\0 \0 \0 \0 \0 \0 373 373 373 373 363 364 353 372 344 024
0000100 334 112 324 245 315 053 305 344 276 330 270 016 261 215 253 133
334 J 324 245 315 + 305 344 276 330 270 016 261 215 253 [
0000120 245 176 237 375 232 335 226 043 221 324 215 364 212 210 207 222
245 ~ 237 375 232 335 226 # 221 324 215 364 212 210 207 222
0000140 205 026 203 026 201 224 200 222 200 021 200 021 200 222 201 224
205 026 203 026 201 224 200 222 200 021 200 021 200 222 201 224
0000160 203 026 205 026 207 222 212 210 215 364 221 324 226 043 232 335
203 026 205 026 207 222 212 210 215 364 221 324 226 # 232 335
0000200 237 375 245 176 253 133 261 215 270 016 276 330 305 344 315 053
237 375 245 ~ 253 [ 261 215 270 016 276 330 305 344 315 +
0000220 324 245 334 112 344 024 353 372 363 364 373 373 004 005 014 014
324 245 334 J 344 024 353 372 363 364 373 373 004 005 \f \f
and so on, but I still get silence from the Quicktime player.
regards
I did not make any such assumption, no. The *1 just lingered from some testing.
[snip]
> and so on, but I still get silence from the Quicktime player.
I don't know, sorry.
It might be that [simple_sound] also needs the "from __future__" treatment.
But anyway, with 2.x compatible code you can now see that the sample values
produced are correct (just print them, or graph them).
I have now installed CPython 2.6.4 and the code presented upthread (the one with
divisions fixed for 2.x compatibility) worked nicely with CPython 2.6.4, even
though with a deprecation warning.
It produced the exact same sound file (bytes) as with CPython 3.1.1.
And the QuickTime 7.3 player played it with no problem, a base A pure sinewave,
composed utterly of 50% duty cycle square waves, resounding in my headphones --
it also works with WinAmp 5.54, Windows Mediaplayer 10.00 and VLC 0.9.9, i.e.
I'm unable to reproduce the problem.
So I suspect that you may lack a suitable codec, or it might be a PEBKAC problem?
Cheers & hth.,
- Alf
PS: I had expected some jaggies and such since I only divided the cycle into 100
intervals, but graphing the data in Excel it showed a perfect sine. :-)
The original code I posted was only written for Python 3.1.1 (because the code
was for my writings which assumes 3.x). In the simple_sound module this caused a
deprecation warning with 2.x. And the example program didn't work with 2.x.
I've now installed CPython 2.6.4 and fixed the code so that it works nicely also
with that version of Python.
<code file="simple_sound.py">
"Generate simple mono (single-channel) [.wav], [.aiff] or [.aifc] files."
# Works with Python 2.6.4 and Python 3.1.1, but has not been extensively tested.
# Author: Alf P. Steinbach.
#
# Changes from original 3.1.1 version:
# * A deprecation warning suppressed by explicit cast to int.
# * The default sound library is now not imported until it's actually used.
# * Added exception handling (for the code's original purpose I couldn't).
#
# Notes:
# (1) It might be possible to optimize this by using array of 16-bit integers, then
# checking 'sys.byteorder' and doing a 'data.byteswap()' call as appropriate.
# (2) Data is kept in memory until 'close' due to a bug in the 'wave' module.
That bug
# has now been fixed. But it may be present in any Python installation.
import collections
import array
import math
default_sample_rate = 44100 # Usual CD quality.
def sample_sawtooth( freq, t ):
linear = freq*t % 1.0
return 2*linear - 1.0
def sample_square( freq, t ):
linear = freq*t % 1.0
if linear < 0.5:
return -1.0
else:
return 1.0
def sample_triangle( freq, t ):
linear = freq*t % 1.0
if linear < 0.5:
return 4.0*linear - 1.0
else:
return 3.0 - 4.0*linear
def sample_sine( freq, t ):
return math.sin( 2*math.pi*freq*t )
DataFormat = collections.namedtuple( "DataFormat",
"open_func, append_int16_func"
)
def _append_as_big_endian_int16_to( bytes_array, i ):
if i < 0:
i = i + 65536
assert( 0 <= i < 65536 )
bytes_array.append( i // 256 )
bytes_array.append( i % 256 )
def _append_as_little_endian_int16_to( bytes_array, i ):
if i < 0:
i = i + 65536
assert( 0 <= i < 65536 )
bytes_array.append( i % 256 )
bytes_array.append( i // 256 )
def aiff_format():
import aifc
return DataFormat( aifc.open, _append_as_big_endian_int16_to )
def wav_format():
import wave
return DataFormat( wave.open, _append_as_little_endian_int16_to )
class Writer:
"Writes normalized samples to a specified file or file-like object"
def __init__(
self,
filename,
sample_rate = default_sample_rate,
data_format = None
):
if data_format is None:
data_format = aiff_format()
self._sample_rate = sample_rate
self._append_int16_func = data_format.append_int16_func
self._writer = data_format.open_func( filename, "w" )
self._writer.setnchannels( 1 )
self._writer.setsampwidth( 2 ) # 2 bytes = 16 bits
self._writer.setframerate( sample_rate )
self._samples = []
def sample_rate( self ):
return self._sample_rate
def write( self, normalized_sample ):
assert( -1 <= normalized_sample <= +1 )
self._samples.append( normalized_sample )
def close( self ):
try:
data = array.array( "B" ) # B -> unsigned bytes.
append_int16_to = self._append_int16_func
for sample in self._samples:
level = int( round( 32767*sample ) )
append_int16_to( data, level )
self._writer.setnframes( len( self._samples ) )
self._writer.writeframes( data )
finally:
self._writer.close()
def example( filename = "ringtone.aiff" ):
global default_sample_rate
sample_rate = default_sample_rate
total_time = 2
n_samples = sample_rate*total_time
writer = Writer( filename )
for i in range( n_samples ):
t = 1.0*i/sample_rate
samples = (
sample_sine( 440, t ),
sample_sine( (5.0/4.0)*440, t ),
)
sample = sum( samples )/len( samples )
writer.write( sample )
writer.close()
if __name__ == "__main__":
example()
</code>
Cheers, & enjoy!,
- Alf
Now now children...
Bullshit. My Boehm (B\"ohm) electronic organ does exactly that.
They even have a chip for it. In the 70's it was a great hype,
a sawtooth organ. Well not exactly a hype, the sound, especially
the low registers, is dramatically better.
If you're interested in frequencies above audible (organ builders
aren't), you need an infinity of squares to build a perfect
sawtooth. But then you need an inifinity of sines to build a
perfect square wave.
<SNIP>
> Mel.
Groetjes Albert
--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst