ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/PDL-Audio/audio.pd
(Generate patch)

Comparing PDL-Audio/audio.pd (file contents):
Revision 1.1 by root, Tue Dec 28 01:05:16 2004 UTC vs.
Revision 1.10 by root, Wed Apr 18 08:28:18 2012 UTC

1$VERSION = '1.01'; 1$VERSION = '1.2';
2 2
3pp_setversion $VERSION; 3pp_setversion $VERSION;
4pp_beginwrap (); # force error with older PPs 4pp_beginwrap (); # force error with older PPs
5 5
6pp_addpm {At => Top}, <<'EOD'; 6pp_addpm {At => Top}, <<'EOD';
13 use PDL; 13 use PDL;
14 use PDL::Audio; 14 use PDL::Audio;
15 15
16=head1 DESCRIPTION 16=head1 DESCRIPTION
17 17
18Oh well ;) Not much "introductury documentation" has been written yet! See 18Oh well ;) Not much "introductory documentation" has been written yet :(
19my other modules for even worse documentation ;) 19
20Installing this distribution also installs F<pdlaudio-demo>, which
21showcases some of the oeprators, and C<pdlaudio-birds>, which imites some
22bird calls with PDL::Audio. You should study them to get the hang of it.
20 23
21=head2 NOTATION 24=head2 NOTATION
22 25
23Brackets around parameters indicate that the respective parameter is 26Brackets around parameters indicate that the respective parameter is
24optional and will be replaced with some default value when absent (or 27optional and will be replaced with some default value when absent (or
33 36
34 # Syntax: gen_oscil duration*, frequency/ 37 # Syntax: gen_oscil duration*, frequency/
35 $signal = gen_oscil 2*HZ, 440/HZ; 38 $signal = gen_oscil 2*HZ, 440/HZ;
36 # with a sampling frequency of 44100 Hertz: 39 # with a sampling frequency of 44100 Hertz:
37 $signal = gen_oscil 2*44100, 440/44100; 40 $signal = gen_oscil 2*44100, 440/44100;
41
42 print describe_audio $signal, "\n";
43 playaudio $signal->scale2short;
38 44
39To help you, the required unit is given as a type suffix in the parameter 45To help you, the required unit is given as a type suffix in the parameter
40name. A "/" means that you have to divide by the sampling frequency (to 46name. A "/" means that you have to divide by the sampling frequency (to
41convert from Hertz) and a suffix of "*" indicates that a multiplication is 47convert from Hertz) and a suffix of "*" indicates that a multiplication is
42required. 48required.
276 282
277=cut 283=cut
278 284
279sub describe_audio($) { 285sub describe_audio($) {
280 my $pdl = shift; 286 my $pdl = shift;
281 my ($samples, $channels) = $pdl->dims; 287 my ($channels, $samples) = $pdl->dims;
288 ($channels, $samples) = (1, $channels) unless defined $samples;
282 my $chan = $channels < 2 ? "mono" : 289 my $chan = $channels < 2 ? "mono" :
283 $channels == 2 ? "stereo" : 290 $channels == 2 ? "stereo" :
284 $channels == 4 ? "quad channel" : 291 $channels == 4 ? "quad channel" :
285 "$channel channel"; 292 "$channels channel";
286 my $desc = "$chan sound with $samples samples"; 293 my $desc = "$chan sound with $samples samples";
287 $desc .= sprintf ", original name \"%s\"", $pdl->path if $pdl->path; 294 $desc .= sprintf ", original name \"%s\"", $pdl->path if $pdl->path;
288 $desc .= sprintf ", type %d (%s)", $pdl->filetype, sound_type_name($pdl->filetype) if $pdl->filetype; 295 $desc .= sprintf ", type %d (%s)", $pdl->filetype, sound_type_name($pdl->filetype) if $pdl->filetype;
289 $desc .= sprintf ", rate %d/s (duration %.2fs)", $pdl->rate, $samples/$pdl->rate if $pdl->rate; 296 $desc .= sprintf ", rate %d/s (duration %.2fs)", $pdl->rate, $samples/$pdl->rate if $pdl->rate;
290 $desc .= sprintf ", format %d (%s)", $pdl->format, sound_format_name($pdl->format) if $pdl->format; 297 $desc .= sprintf ", format %d (%s)", $pdl->format, sound_format_name($pdl->format) if $pdl->format;
291 $desc; 298 $desc
292} 299}
293 300
294=head2 raudio path, [option-hash], option => value, ... 301=head2 raudio path, [option-hash], option => value, ...
295 302
296Reads audio data into the piddle. Options can be anything, most useful values are 303Reads audio data into the piddle. Options can be anything, most useful
297C<filetype>, C<rate>, C<channels> and C<format>. 304values are C<filetype>, C<rate>, C<channels> and C<format>. The returned
305piddle is represents "time" in the outer dimension, and samples in the
306inner (i.e. scalars for mono files, 2-vectors for stereo files):
307
308 [ [left0, right0], [left1, right1], [left2, right2], ...]
298 309
299 # read any file 310 # read any file
300 $pdl = raudio "file.wav"; 311 $pdl = raudio "file.wav";
301 # read a file. if it is a raw file preset values 312 # read a file. if it is a raw file preset values
302 $pdl = raudio "file.raw", filetype => FILE_RAW, rate => 44100, channels => 2; 313 $pdl = raudio "file.raw", filetype => FILE_RAW, rate => 44100, channels => 2;
303 314
304=head2 waudio pdl, [option-hash], option => value, ... 315=head2 waudio pdl, [option-hash], option => value, ...
305 316
306Writes a pdl as a file. The path is taken from the header (or the options), e.g.: 317Writes a pdl as a file. See L<raudio> for options and format. The path and
318other metadata is taken from the header, whcih cna be overwritten using
319options, e.g.:
307 320
308 # write a file, using the header of another piddle 321 # write a file, using the header of another piddle
309 $pdl->waudio($orig_file->gethdr); 322 $pdl->waudio ($orig_file->gethdr);
310 # write pdl as au file, take rate from the header 323 # write pdl as .au file, take rate from the header
311 $pdl->waudio(path => "piddle.au", filetype => FILE_AU, format => FORMAT_16_LINEAR; 324 $pdl->waudio (path => "piddle.au", filetype => FILE_AU, format => FORMAT_16_LINEAR;
312 325
313=cut 326=cut
314 327
315# read a sound file 328# read a sound file
316sub raudio { 329sub raudio {
339 (close_sound_input $fd) >= 0 or barf "$path: ".audio_error_name audio_error; 352 (close_sound_input $fd) >= 0 or barf "$path: ".audio_error_name audio_error;
340 $pdl = $pdl->short->xchg(0,1); 353 $pdl = $pdl->short->xchg(0,1);
341 $pdl = $pdl->clump(2) if $channels == 1; 354 $pdl = $pdl->clump(2) if $channels == 1;
342 $pdl->sever; 355 $pdl->sever;
343 $pdl->sethdr(\%hdr); 356 $pdl->sethdr(\%hdr);
344 $pdl; 357 $pdl
345} 358}
346 359
347sub _audio_make_plain { 360sub _audio_make_plain {
348 my $pdl = shift; 361 my $pdl = shift;
349 if ($pdl->getndims == 1) { 362 if ($pdl->getndims == 1) {
350 ($pdl, 1, $pdl->getdim(0)); 363 ($pdl, 1, $pdl->getdim(0))
351 } else { 364 } else {
352 ($pdl->xchg(0,1)->clump(-1), ($pdl->dims)[1,0]); 365 ($pdl->xchg(0,1)->clump(-1), $pdl->dims)
353 } 366 }
354} 367}
355 368
356sub waudio { 369sub waudio {
357 my $pdl = shift; 370 my $pdl = shift;
362 $hdr{format} ||= FORMAT_16_LINEAR; 375 $hdr{format} ||= FORMAT_16_LINEAR;
363 $hdr{rate} ||= 44100; 376 $hdr{rate} ||= 44100;
364 377
365 ($pdl, $channels, $frames) = _audio_make_plain $pdl->convert(long); 378 ($pdl, $channels, $frames) = _audio_make_plain $pdl->convert(long);
366 379
380 1 <= $channels && $channels <= 2
381 or croak "can only write mono or stereo (one or two channel) files, not $channels channel files";
382
367 my $fd = open_sound_output $hdr{path}, $hdr{rate}, $channels, $hdr{format}, $hdr{filetype}, $hdr{comment}; 383 my $fd = open_sound_output $hdr{path}, $hdr{rate}, $channels, $hdr{format}, $hdr{filetype}, $hdr{comment};
368 $fd >= 0 or barf "$hdr{$path}: ".audio_error_name audio_error; 384 $fd >= 0 or barf "$hdr{$path}: ".audio_error_name audio_error;
369 $pdl->clump(-1)->write_sound($fd, $channels, $frames) 385 $pdl->clump(-1)->write_sound($fd, $channels, $frames)
370 >= 0 or barf "$path: ".audio_error_name audio_error; 386 >= 0 or barf "$path: ".audio_error_name audio_error;
371 (close_sound_output $fd, mus_samples2bytes $hdr{format}, $frames) 387 (close_sound_output $fd, mus_samples2bytes $hdr{format}, $frames * $channels)
372 >= 0 or barf "$hdr{$path}: ".audio_error_name audio_error; 388 >= 0 or barf "$hdr{$path}: ".audio_error_name audio_error;
373} 389}
374 390
375=head2 cut_leading_silence pdl, level 391=head2 cut_leading_silence pdl, level
376 392
391sub cut_leading_silence { 407sub cut_leading_silence {
392 my $pdl = shift; 408 my $pdl = shift;
393 my $level = 1*shift; 409 my $level = 1*shift;
394 my $skip = which (abs($pdl) > $level); 410 my $skip = which (abs($pdl) > $level);
395 $skip = $skip->nelem ? $skip->at(0) : 0; 411 $skip = $skip->nelem ? $skip->at(0) : 0;
396 $pdl->slice("$skip:-1"); 412 $pdl->slice("$skip:-1")
397} 413}
398 414
399sub cut_trailing_silence { 415sub cut_trailing_silence {
400 my $pdl = shift; 416 my $pdl = shift;
401 my $level = 1*shift; 417 my $level = 1*shift;
402 $level = 400000; 418 $level = 400000;
403 my $skip = which (abs($pdl) > $level); 419 my $skip = which (abs($pdl) > $level);
404 $skip = $skip->nelem ? $skip->at(-1) : -1; 420 $skip = $skip->nelem ? $skip->at(-1) : -1;
405 $skip-- if $skip > 0; 421 $skip-- if $skip > 0;
406 $pdl->slice("0:$skip"); 422 $pdl->slice("0:$skip")
407} 423}
408 424
409sub cut_silence { 425sub cut_silence {
410 $_[0]->cut_leading_silence($_[1]) 426 $_[0]->cut_leading_silence($_[1])
411 ->cut_trailing_silence($_[1]); 427 ->cut_trailing_silence($_[1])
412} 428}
413 429
414# have we been a bad boy? 430# have we been a bad boy?
415 431
416for (@METHODS) { 432for (@METHODS) {
753in C<table>, linearly interpolating between successive points of the 769in C<table>, linearly interpolating between successive points of the
754C<waveform>. 770C<waveform>.
755 771
756=head2 partials2waveshape size*, partials, amplitudes, [phase], [fm_mod/] 772=head2 partials2waveshape size*, partials, amplitudes, [phase], [fm_mod/]
757 773
758Take a list (perl list or pdl) of (integer) C<partials> and a list of 774Take a (perl or pdl) list of (integer) C<partials> and a list of
759C<amplitudes> and generate a single wave shape that results by adding 775C<amplitudes> and generate a single wave shape that results by adding
760these partial sines. 776these partial sines.
761 777
762This could (and should) be used by the C<gen_from_table> generator. 778This could (and should) be used by the C<gen_from_table> generator.
763 779
764=head2 gen_from_partials duration*, frequency/, partials, amplitudes, [phase], [fm_mod/] 780=head2 gen_from_partials duration*, frequency/, partials, amplitudes, [phase], [fm_mod/]
765 781
766Take a list (perl list or pdl) of (possibly noninteger) C<partials> and a 782Take a (perl list or pdl) list of (possibly noninteger) C<partials> and a
767list of C<amplitudes> and generate the waveform resulting by summing up 783list of C<amplitudes> and generate the waveform resulting by summing up
768all these partial sines. 784all these partial sines.
769 785
770=cut 786=cut
771 787
1209Calculates the optimal (in the Chebyshev/minimax sense) FIR filter 1225Calculates the optimal (in the Chebyshev/minimax sense) FIR filter
1210impulse response given a set of band edges, the desired reponse on those 1226impulse response given a set of band edges, the desired reponse on those
1211bands, and the weight given to the error in those bands, using the 1227bands, and the weight given to the error in those bands, using the
1212Parks-McClellan exchange algorithm. 1228Parks-McClellan exchange algorithm.
1213 1229
1214The first argument (the one with the funny name) sets the filter 1230The first argument sets the filter size: C<design_remez_fir> returns as
1215size: C<design_remez_fir> returns as many coefficients as specified via 1231many coefficients as specified by this parameter.
1216this parameter.
1217 1232
1218C<bands> is a vector of band edge pairs (start - end), who specify the 1233C<bands> is a vector of band edge pairs (start - end), which specify the
1219start and end of the bands in the filter specification. These must be 1234start and end of the bands in the filter specification. These must be
1220non-overlapping and sorted in increasing order. Only values between C<0> 1235non-overlapping and sorted in increasing order. Only values between C<0>
1221(0 Hz) and C<0.5> (the Nyquist frequency) are allowed. 1236(0 Hz) and C<0.5> (the Nyquist frequency) are allowed.
1222 1237
1223C<des> specifies the desired gain in these bands. 1238C<des> specifies the desired gain in these bands.
1500 $beta = 2.5 unless defined $beta; 1515 $beta = 2.5 unless defined $beta;
1501 1516
1502 $size = $size->getdim(0) if ref $size; 1517 $size = $size->getdim(0) if ref $size;
1503 $size > 2 or barf "fft window size too small"; 1518 $size > 2 or barf "fft window size too small";
1504 1519
1505 my $midn = $size >> 1; 1520 my $midn = $size >> 1;
1506 my $midm1 = ($size-1) >> 1; 1521 my $midm1 = ($size-1) >> 1;
1507 my $midp1 = ($size+1) >> 1; 1522 my $midp1 = ($size+1) >> 1;
1508 my $dur = zeroes $size; 1523 my $dur = zeroes $size;
1509 my $sf = ($size-1)/$size; 1524 my $sf = ($size-1)/$size;
1510 %fft_window = ( 1525 %fft_window = (
1511 RECTANGULAR => sub { 1526 RECTANGULAR => sub {
1512 $dur->ones 1527 $dur->ones
1513 }, 1528 },
1615my $fftw_loaded; 1630my $fftw_loaded;
1616sub _fftw { 1631sub _fftw {
1617 defined $fftw_loaded or eval { 1632 defined $fftw_loaded or eval {
1618 $fftw_loaded = 0; 1633 $fftw_loaded = 0;
1619 require PDL::FFTW; 1634 require PDL::FFTW;
1620 PDL::FFTW::load_wisdom("$ENV{HOME}/.pdl_wisdom") if $ENV{HOME}; 1635 PDL::FFTW::load_wisdom("$ENV{HOME}/.pdl_wisdom")
1636 if -r "$ENV{HOME}/.pdl_wisdom";
1621 $fftw_loaded = 1; 1637 $fftw_loaded = 1;
1622 }; 1638 };
1623 $fftw_loaded; 1639 $fftw_loaded;
1624} 1640}
1625 1641
1626sub rfft { 1642sub rfft {
1627 my $data = $_[0]; 1643 my $data = $_[0];
1628 if (_fftw) { 1644 if (_fftw) {
1629 my $x = $data->r2C; 1645 my $x = $data->r2C;
1630 PDL::FFTW::nfftw($x); 1646 $x = PDL::FFTW::fftw $x;
1631 $x; 1647 $x;
1632 } else { 1648 } else {
1633 require PDL::FFT; 1649 require PDL::FFT;
1634 my @fft = ($data->copy, $data->zeroes); 1650 my @fft = ($data->copy, $data->zeroes);
1635 PDL::FFT::fft(@fft); 1651 PDL::FFT::fft(@fft);
1638} 1654}
1639 1655
1640sub irfft { 1656sub irfft {
1641 if (_fftw) { 1657 if (_fftw) {
1642 $x = $_[0]->copy; 1658 $x = $_[0]->copy;
1643 PDL::FFTW::infftw($x); 1659 $x = PDL::FFTW::ifftw $x;
1644 re $x / $x->getdim(1); 1660 re $x / $x->getdim(1);
1645 } else { 1661 } else {
1646 require PDL::FFT; 1662 require PDL::FFT;
1647 my @fft = $_[0]->xchg(0,1)->dog({Break => 1}); 1663 my @fft = $_[0]->xchg(0,1)->dog({Break => 1});
1648 PDL::FFT::ifft(@fft); 1664 PDL::FFT::ifft(@fft);
1652 1668
1653=head2 spectrum data, [norm], [window], [beta] 1669=head2 spectrum data, [norm], [window], [beta]
1654 1670
1655Returns the spectrum of a given pdl. If C<norm> is absent (or C<undef>), 1671Returns the spectrum of a given pdl. If C<norm> is absent (or C<undef>),
1656it returns the magnitude of the fft of C<data>. When C<norm> == 1 (or 1672it returns the magnitude of the fft of C<data>. When C<norm> == 1 (or
1657C<eq 'NORM'>, in any case), it returns the magnitude, normalized to be 1673C<eq 'NORM'>, case-insensitive), it returns the magnitude, normalized to be
1658between zero and one. If C<norm> == 0 (or C<eq 'dB'>, in any case), then 1674between zero and one. If C<norm> == 0 (or C<eq 'dB'>, case-insensitive), then
1659it returns the magnitude in dB. 1675it returns the magnitude in dB.
1660 1676
1661C<data> is multiplied with C<window> (if not C<undef>) before calculating 1677C<data> is multiplied with C<window> (if not C<undef>) before calculating
1662the fft, and usually contains a window created with C<gen_fft_window> 1678the fft, and usually contains a window created with C<gen_fft_window>
1663(using C<beta>). If C<window> is a string, it is handed over to 1679(using C<beta>). If C<window> is a string, it is handed over to
1670 1686
1671sub spectrum { 1687sub spectrum {
1672 my ($data, $norm, $window, $beta) = @_; 1688 my ($data, $norm, $window, $beta) = @_;
1673 my $len; 1689 my $len;
1674 if (defined $window) { 1690 if (defined $window) {
1675 $window = gen_fft_window ($data->getdim(0), $window, $beta) unless ref $window; 1691 $window = gen_fft_window ($data->getdim (0), $window, $beta) unless ref $window;
1676 $data = $data * $window; 1692 $data = $data * $window;
1677 $len = $window->getdim(0); 1693 $len = $window->getdim (0);
1678 } else { 1694 } else {
1679 $len = $data->getdim(0); 1695 $len = $data->getdim (0);
1680 } 1696 }
1681 $data = rfft ($data->slice("0:".($len-1))->sever)->slice(",0:".int($len/2))->Cr2p->slice("(0)"); 1697 $data = rfft (
1698 $data->slice ("0:" . ($len - 1))
1699 ->sever
1700 )
1701 ->slice (",0:" . int ($len / 2))
1702 ->PDL::Complex::Cr2p
1703 ->slice ("(0)");
1682 if ($norm == 1 || lc $norm eq "norm") { 1704 if ($norm == 1 || lc $norm eq "norm") {
1683 $data / max $data; 1705 $data / max $data;
1684 } elsif (($norm =~ /^[.0]+$/) || (lc $norm eq "db")) { 1706 } elsif (($norm =~ /^[.0]+$/) || (lc $norm eq "db")) {
1685 log (1e-37 + $data / max $data) * (20 / log 10); 1707 log (1e-37 + $data / max $data) * (20 / log 10);
1686 } else { 1708 } else {
1884 1906
1885pp_addpm {At => Bot}, <<'EOD'; 1907pp_addpm {At => Bot}, <<'EOD';
1886 1908
1887=head1 AUTHOR 1909=head1 AUTHOR
1888 1910
1889Marc Lehmann <pcg@goof.com>. The ideas were mostly taken from common 1911Marc Lehmann <schmorp@schmorp.de>. The ideas were mostly taken from common
1890lisp music (CLM), by Bill Schottstaedt C<bil@ccrma.stanford.edu>. I also 1912lisp music (CLM), by Bill Schottstaedt C<bil@ccrma.stanford.edu>. I also
1891borrowed many explanations (and references) from the clm docs and some 1913borrowed many explanations (and references) from the clm docs and some
1892code from clm.c. Highly inspiring! 1914code from clm.c. Highly inspiring!
1893 1915
1894=head1 SEE ALSO 1916=head1 SEE ALSO

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines