| Audio-Wav documentation | Contained in the Audio-Wav distribution. |
Audio::Wav::Write - Module for writing Microsoft WAV files.
use Audio::Wav;
my $wav = new Audio::Wav;
my $sample_rate = 44100;
my $bits_sample = 16;
my $details = {
'bits_sample' => $bits_sample,
'sample_rate' => $sample_rate,
'channels' => 1,
# if you'd like this module not to use a write cache, uncomment the next line
#'no_cache' => 1,
};
my $write = $wav -> write( 'testout.wav', $details );
&add_sine( 200, 1 );
sub add_sine {
my $hz = shift;
my $length = shift;
my $pi = ( 22 / 7 ) * 2;
$length *= $sample_rate;
my $max_no = ( 2 ** $bits_sample ) / 2;
for my $pos ( 0 .. $length ) {
$time = $pos / $sample_rate;
$time *= $hz;
my $val = sin $pi * $time;
my $samp = $val * $max_no;
$write -> write( $samp );
}
}
$write -> finish();
Currently only writes to a file.
This module shouldn't be used directly, a blessed object can be returned from Audio::Wav.
Finishes off & closes the current wav file.
$write -> finish();
Adds a cue point to the wav file. If $sample is undefined then the position will be the current position (end of all data written so far).
# $byte_offset for 01 compatibility mode
$write -> add_cue( $sample, "label", "note" );
All parameters are optional.
my %info = (
'midi_pitch_fraction' => 0,
'smpte_format' => 0,
'smpte_offset' => 0,
'product' => 0,
'sample_period' => 0,
'manufacturer' => 0,
'sample_data' => 0,
'midi_unity_note' => 65,
);
$write -> set_sampler_info( %info );
All parameters are optional except start & end.
my $length = $read -> length_samples();
my( $third, $twothirds ) = map int( $length / $_ ), ( 3, 1.5 );
my %loop = (
'start' => $third,
'end' => $twothirds,
'fraction' => 0,
'type' => 0,
);
$write -> add_sampler_loop( %loop );
Sets information to be contained in the wav file.
$write -> set_info( 'artist' => 'Nightmares on Wax', 'name' => 'Mission Venice' );
Returns the current filename.
my $file = $write -> file_name();
Adds a sample to the current file.
$write -> write( @sample_channels );
Each element in @sample_channels should be in the range of;
where $samp_max = ( 2 ** bits_per_sample ) / 2
-$samp_max to +$samp_max
Adds some pre-packed data to the current file.
$write -> write_raw( $data, $data_length );
Where;
$data is the packed data
$data_length (optional) is the length in bytes of the data
Adds some pre-packed data to the current file, returns number of samples written.
$write -> write_raw_samples( $data, $data_length );
Where;
$data is the packed data
$data_length (optional) is the length in bytes of the data
Nick Peskett (see http://www.peskett.co.uk/ for contact details).
Kurt George Gjerde <kurt.gjerde@media.uib.no>. (0.02-0.03)
| Audio-Wav documentation | Contained in the Audio-Wav distribution. |
package Audio::Wav::Write; use strict; eval { require warnings; }; #it's ok if we can't load warnings use FileHandle; use Audio::Wav::Write::Header; use vars qw( $VERSION ); $VERSION = '0.12';
sub new { my $class = shift; my $out_file = shift; my $details = shift; my $tools = shift; my $handle = new FileHandle ">$out_file"; my $use_cache = 1; if ( ref $details eq 'HASH' && exists $details -> {'no_cache'} ) { my $no_cache = delete $details -> {'no_cache'}; $use_cache = 0 if $no_cache; } my $self = { 'use_cache' => $use_cache, 'write_cache' => undef, 'out_file' => $out_file, 'cache_size' => 4096, 'handle' => $handle, 'details' => $details, 'block_align' => $details -> {'block_align'}, 'tools' => $tools, 'done_finish' => 0, }; bless $self, $class; unless ( defined $handle ) { my $error = $!; chomp $error; $self -> _error( "unable to open file ($error)" ); return $self; } binmode $handle; $self -> _init(); $self -> _start_file(); $self -> _examine_details( $details ); if ( $self -> {'details'} -> {'bits_sample'} <= 8 ) { $self -> {'use_offset'} = ( 2 ** $self -> {'details'} -> {'bits_sample'} ) / 2; } else { $self -> {'use_offset'} = 0; } return $self; } sub DESTROY { my $self = shift; return unless $self; return if $self -> {'done_finish'}; $self -> finish(); }
sub finish { my $self = shift; $self -> _purge_cache() if $self -> {'use_cache'}; $self -> {'header'} -> finish( $self -> {'pos'} ); $self -> {'handle'} -> close(); $self -> {'done_finish'} = 1; }
sub add_cue { my $self = shift; my $pos = shift; my $label = shift; my $note = shift; my $block_align = $self -> {'details'} -> {'block_align'}; if ( defined $pos ) { $pos /= $block_align if $self -> {'tools'} -> is_01compatible(); } else { $pos = $self -> {'pos'} / $block_align; } my $output = { 'pos' => $pos, }; $output -> {'label'} = $label if $label; $output -> {'note'} = $note if $note; $self -> {'header'} -> add_cue( $output ); }
sub set_sampler_info { my ($self, @args) = @_; return $self -> {'header'} -> set_sampler_info( @args ); }
sub add_sampler_loop { my ($self, @args) = @_; return $self -> {'header'} -> add_sampler_loop( @args ); }
sub add_display { my ($self, @args) = @_; return $self -> {'header'} -> add_display( @args ); }
sub set_info { my ($self, %info) = @_; $self -> {'details'} -> {'info'} = { %{ $self -> {'details'} -> {'info'} }, %info }; }
sub file_name { my $self = shift; return $self -> {'out_file'}; }
sub write { my ($self, @args) = @_; my $channels = $self -> {'details'} -> {'channels'}; if ( $self -> {'use_offset'} ) { return $self -> write_raw( pack 'C'.$channels, map { $_ + $self -> {'use_offset'} } @args ); } else { return $self -> write_raw( pack 'v'.$channels, @args ); } }
sub write_raw { my $self = shift; my $data = shift; my $len = shift; $len = length $data unless $len; return unless $len; my $wrote = $len; if ( $self -> {'use_cache'} ) { $self -> {'write_cache'} .= $data; my $cache_len = length $self -> {'write_cache'}; $self -> _purge_cache( $cache_len ) unless $cache_len < $self -> {'cache_size'}; } else { $wrote = syswrite $self -> {'handle'}, $data, $len; } $self -> {'pos'} += $wrote; return $wrote; }
sub write_raw_samples { my ($self, @args) = @_; my $written = $self -> write_raw( @args ); return $written / $self -> {'details'} -> {'block_align'}; } #################### sub _start_file { my $self = shift; my( $file, $details, $tools, $handle ) = map { $self -> {$_} } qw( out_file details tools handle ); my $header = Audio::Wav::Write::Header -> new( $file, $details, $tools, $handle ); $self -> {'header'} = $header; my $data = $header -> start(); $self -> write_raw( $data ); $self -> {'pos'} = 0; } sub _purge_cache { my $self = shift; my $len = shift; return unless $self -> {'write_cache'}; my $cache = $self -> {'write_cache'}; $len = length $cache unless $len; my $res = syswrite $self -> {'handle'}, $cache, $len; $self -> {'write_cache'} = undef; } sub _init { my $self = shift; my $details = $self -> {'details'}; my $output = {}; my @missing; my @needed = qw ( bits_sample channels sample_rate ); my @wanted = qw ( block_align bytes_sec info wave-ex ); foreach my $need ( @needed ) { if ( exists( $details -> {$need} ) && $details -> {$need} ) { $output -> {$need} = $details -> {$need}; } else { push @missing, $need; } } return $self -> _error('I need the following parameters supplied: ' . join ', ', @missing ) if @missing; foreach my $want ( @wanted ) { next unless ( exists( $details -> {$want} ) && $details -> {$want} ); $output -> {$want} = $details -> {$want}; } unless ( exists $details -> {'block_align'} ) { my( $channels, $bits ) = map { $output -> {$_} } qw( channels bits_sample ); my $mod_bits = $bits % 8 ? 1 : 0; $mod_bits += int $bits / 8; $output -> {'block_align'} = $channels * $mod_bits; } unless ( exists $output -> {'bytes_sec'} ) { my( $rate, $block ) = map { $output -> {$_} } qw( sample_rate block_align ); $output -> {'bytes_sec'} = $rate * $block; } unless ( exists $output -> {'info'} ) { $output -> {'info'} = {}; } $self -> {'details'} = $output; } sub _examine_details { my $self = shift; my $details = shift; my( $cue, $label, $note ) = map { exists( $details -> {$_} ) ? $details -> {$_} : {} } qw( cue labl note ); my $block_align = $self -> {'details'} -> {'block_align'}; my $tools = $self -> {'tools'}; foreach my $id ( sort keys %{$cue} ) { # <-- Thanks to jeremyd713@hotmail.com my $pos = $cue -> {$id} -> {'position'}; $pos *= $block_align if $tools -> is_01compatible(); my( $in_label, $in_note ) = map { exists( $_ -> {$id} ) ? $_ -> {$id} : '' } ( $label, $note ); $self -> add_cue( $pos, $in_label, $in_note ); } if ( exists $details -> {'sampler'} ) { my $sampler = $details -> {'sampler'}; my $loops = delete $sampler -> {'loop'}; $self -> set_sampler_info( %{$sampler} ); foreach my $loop ( @{$loops} ) { $self -> add_sampler_loop( %{$loop} ); } } if ( exists $details -> {'display'} ) { my @display = @{ $details -> {'display'} }; my @fields = qw( id data ); $self -> add_display( map { $fields[$_] => $display[$_] } 0, 1 ); } } sub _error { my ($self, @args) = @_; return $self -> {'tools'} -> error( $self -> {'out_file'}, @args ); }
1;