The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Audio::PSID - Perl module to handle PlaySID files (Commodore-64 music files), commonly known as SID files.

SYNOPSIS

    use Audio::PSID;

    $myPSID = new Audio::PSID ("Test.sid") or die "Whoops!";

    print "Name = " . $myPSID->get('name') . "\n";

    print "MD5 = " . $myPSID->getMD5();

    $myPSID->set(author => 'LaLa',
                 name => 'Test2',
                 copyright => '2001 Hungarian Music Crew');

    $myPSID->validate();
    $myPSID->write("Test2.sid") or die "Couldn't write file!";

    @array = $myPSID->getFieldNames();
    print "Fieldnames = " . join(' ', @array) . "\n";

DESCRIPTION

This module is designed to handle PlaySID files (usually bearing a .sid extension), which are music player and data routines converted from the Commodore-64 computer with an additional informational header prepended. For further details about the exact file format, see description of all PSID fields in the PSID_v2NG.txt file included in the module package. For information about SID tunes in general, see the excellent SIDPLAY homepage at:

http://www.geocities.com/SiliconValley/Lakes/5147/

For PSID v2NG documentation:

http://sidplay2.sourceforge.net

You can find literally thousands of SID tunes in the High Voltage SID Collection at:

http://www.hvsc.c64.org

This module can handle both version 1 and version 2/2NG PSID files. (Version 2 files are simply v2NG files where v2NG specific fields are set 0.) The module was designed primarily to make it easier to look at and change the PSID header fields, so many of the member function are geared towards that. Use $OBJECT->getFieldNames() to find out the exact names of the fields currently recognized by this module. Please note that fieldnames are case-sensitive!

Member functions

PACKAGE->new([SCALAR]) or PACKAGE->new([FILEHANDLE])

Returns a newly created PSID object. If neither SCALAR nor FILEHANDLE is specified, the object is initialized with default values. See $OBJECT->initalize() below.

If SCALAR or FILEHANDLE is specified, an attempt is made to open the given file as specified in $OBJECT->read() below.

OBJECT->initialize()

Initializes the object with default PSID data as follows:

    version => 2,
    dataOffset => 0x7C,
    name => '<?>',
    author => '<?>',
    copyright => '20?? <?>',
    data => '',

Every other PSID field (loadAddress, initAddress, playAddress, songs, startSong, speed, flags, startPage, pageLength and reserved) is set to 0. FILENAME is set to '' and the filesize is set to 0x7C.

$OBJECT->read([SCALAR]) or $OBJECT->read([FILEHANDLE])

Reads the PSID file given by the filename SCALAR or by FILEHANDLE and populates the fields with the values taken from this file. If the given file is a PSID version 1 file, the fields of flags, startPage, pageLength and reserved are set to undef.

If neither SCALAR nor FILEHANDLE is specified, the value of FILENAME is used to determine the name of the input file. If that is not set, either, the module is initialized with default data and returns an undef. Note that SCALAR and FILEHANDLE here can be different than the value of FILENAME! If SCALAR is defined, it will overwrite the filename stored in FILENAME, otherwise it is not modified. So, watch out when passing in a FILEHANDLE, because FILENAME will not be modified!

If the file turns out to be an invalid PSID file, the module is initialized with default data and returns an undef. Valid PSID files must have the ASCII string 'PSID' as their first 4 bytes, and either 0x0001 or 0x0002 as the next 2 bytes in big-endian format.

$OBJECT->write([SCALAR]) or $OBJECT->write([FILEHANDLE])

Writes the PSID file given by the filename SCALAR or by FILEHANDLE to disk. If neither SCALAR nor FILEHANDLE is specified, the value of FILENAME is used to determine the name of the output file. If that is not set, either, returns an undef. Note that SCALAR and FILEHANDLE here can be different than the value of FILENAME! If SCALAR is defined, it will not overwrite the filename stored in FILENAME.

write will create a version 1 or version 2/2NG PSID file depending on the value of the version field, regardless of whether the other fields are set correctly or not, or even whether they are undef'd or not. However, if $OBJECT->alwaysValidateWrite(1) was called beforehand, write will always write a validated v2NG PSID file. See below.

$OBJECT->get([SCALAR])

Retrieves the value of the PSID field given by the name SCALAR, or returns a hash of all the recognized PSID fields with their values if called in an array/hash context.

If the fieldname given by SCALAR is unrecognized, the operation is ignored and an undef is returned. If SCALAR is not specified and get is not called from an array context, the same terrible thing will happen. So try not to do either of these.

$OBJECT->getFileName()

Returns the current FILENAME stored in the object.

$OBJECT->getFileSize()

Returns the total size of the PSID file that would be written by $OBJECT->write() if it was called right now. This means that if you read in a version 1 file and changed the version field to 2 without actually saving the file, the size returned here will reflect the size of how big the version 2 file would be.

$OBJECT->getRealLoadAddress()

The "real load address" indicates what is the actual Commodore-64 memory location where the PSID data is going to be loaded into. If loadAddress is non-zero, then loadAddress is returned here, otherwise it's the first two bytes of data (read from there in little-endian format).

$OBJECT->getSpeed([SCALAR])

Returns the speed of the song number specified by SCALAR. If no SCALAR is specified, returns the speed of song #1. Speed can be either 0 (indicating a vertical blank interrupt (50Hz PAL, 60Hz NTSC)), or 1 (indicating CIA 1 timer interrupt (default is 60Hz)).

$OBJECT->getMUSPlayer()

Returns the value of the 'MUSPlayer' bit of the flags field if flags is specified (i.e. when version is 2), or undef otherwise. The returned value is either 0 (indicating a built-in music player) or 1 (indicating that data is a Compute!'s Sidplayer MUS data and the music player must be merged).

$OBJECT->isMUSPlayerRequired()

This is an alias for $OBJECT->getMUSPlayer().

$OBJECT->getPlaySID()

Returns the value of the 'psidSpecific' bit of the flags field if flags is specified (i.e. when version is 2), or undef otherwise. The returned value is either 0 (indicating that data is Commodore-64 compatible) or 1 (indicating that data is PlaySID specific).

$OBJECT->isPlaySIDSpecific()

This is an alias for $OBJECT->getPlaySID().

$OBJECT->getClock()

Returns the value of the 'clock' (video standard) bits of the flags field if flags is specified (i.e. when version is 2), or undef otherwise. The returned value is one of 0 (UNKNOWN), 1 (PAL), 2 (NTSC) or 3 (EITHER).

$OBJECT->getClockByName()

Returns the textual value of the 'clock' (video standard) bits of the flags field if flags is specified (i.e. when version is 2), or undef otherwise. The textual value will be one of UNKNOWN, PAL, NTSC or EITHER.

$OBJECT->getSIDModel()

Returns the value of the 'sidModel' bits of the flags field if flags is specified (i.e. when version is 2), or undef otherwise. The returned value is one of 0 (UNKNOWN), 1 (6581), 2 (8580) or 3 (EITHER).

$OBJECT->getSIDModelByName()

Returns the textual value of the 'sidModel' bits of the flags field if flags is specified (i.e. when version is 2), or undef otherwise. The textual value will be one of UNKNOWN, 6581, 8580 or EITHER.

$OBJECT->set(field => value [, field => value, ...] )

Given one or more field-value pairs it changes the PSID fields given by field to have value.

If you try to set a field that is unrecognized, that particular field-value pair will be ignored. Trying to set the version field to anything other than 1 or 2 will result in criminal prosecution, expulsion, and possibly death... Actually, it won't harm you, but the invalid value will be ignored.

Whenever the version number is changed to 1, the flags, startPage, pageLength and reserved fields are automatically set to be undef'd, and the dataOffset field is reset to 0x0076. Whenever the version number is changed to 2, the flags, startPage, pageLength and reserved fields are zeroed out.

If you try to set flags, startPage, pageLength or reserved when version is not 2, the values will be ignored. Trying to set dataOffset when version is 1 will always reset its value to 0x0076, and dataOffset can't be set to lower than 0x007C if version is 2. You can set it higher, though, in which case either the relevant portion of the original extra padding bytes between the PSID header and the data will be preserved, or additional 0x00 bytes will be added between the PSID header and the data if necessary.

$OBJECT->setFileName([SCALAR])

Sets the FILENAME to SCALAR. This filename is used by $OBJECT->read() and $OBJECT->write() when either one of them is called without any arguments. SCALAR can specify either a relative or an absolute pathname to the file - in fact, it can be anything that can be passed to a FileHandle type object as a filename.

$OBJECT->setSpeed([SCALAR1], [SCALAR2])

Changes the speed of the song number specified by SCALAR1 to that of SCALAR2. SCALAR1 has to be more than 1 and less than the value of the songs field. SCALAR2 can be either 0 (indicating a vertical blank interrupt (50Hz PAL, 60Hz NTSC)), or 1 (indicating CIA 1 timer interrupt (default is 60Hz)). An undef is returned if neither was specified.

$OBJECT->setMUSPlayer([SCALAR])

Changes the value of the 'MUSPlayer' bit of the flags field to SCALAR if flags is specified (i.e. when version is 2), returns an undef otherwise. SCALAR must be either 0 (indicating a built-in music player) or 1 (indicating that data is a Compute!'s Sidplayer MUS data and the music player must be merged).

$OBJECT->setPlaySID([SCALAR])

Changes the value of the 'psidSpecific' bit of the flags field to SCALAR if flags is specified (i.e. when version is 2), returns an undef otherwise. SCALAR must be either 0 (indicating that data is Commodore-64 compatible) or 1 (indicating that data is PlaySID specific).

$OBJECT->setClock([SCALAR])

Changes the value of the 'clock' (video standard) bits of the flags field to SCALAR if flags is specified (i.e. when version is 2), returns an undef otherwise. SCALAR must be one of 0 (UNKNOWN), 1 (PAL), 2 (NTSC) or 3 (EITHER).

$OBJECT->setClockByName([SCALAR])

Changes the value of the 'clock' (video standard) bits of the flags field if flags is specified (i.e. when version is 2), returns an undef otherwise. SCALAR must be be one of UNKNOWN, NONE, NEITHER (all 3 indicating UNKNOWN), PAL, NTSC or ANY, BOTH, EITHER (all 3 indicating EITHER) and is case-insensitive.

$OBJECT->setSIDModel([SCALAR])

Changes the value of the 'sidModel' bits of the flags field if flags is specified (i.e. when version is 2), returns an undef otherwise. SCALAR must be one of 0 (UNKNOWN), 1 (6581), 2 (8580) or 3 (EITHER).

$OBJECT->setSIDModelByName([SCALAR])

Changes the value of the 'sidModel' bits of the flags field if flags is specified (i.e. when version is 2), returns an undef otherwise. SCALAR must be be one of UNKNOWN, NONE, NEITHER (all 3 indicating UNKNOWN), 6581, 8580 or ANY, BOTH, EITHER (all 3 indicating EITHER) and is case-insensitive.

$OBJECT->getFieldNames()

Returns an array that contains the PSID fieldnames recognized by this module, regardless of the PSID version number. All fieldnames are taken from the standard PSID v2NG file format specification, but do not include those fields that are themselves contained in another field, namely any field that is inside the flags field. The fieldname FILENAME is also not returned here, since that is considered to be a descriptive parameter of the PSID file and is not part of the PSID v2NG specification.

$OBJECT->getMD5([SCALAR])

Returns a string containing a hexadecimal representation of the 128-bit MD5 fingerprint calculated from the following PSID fields: data (excluding the first 2 bytes if loadAddress is 0), initAddress, playAddress, songs, the relevant bits of speed, and the value of the clock field if it's set to NTSC and SCALAR is zero or not defined. If SCALAR is a nonzero value, the MD5 fingerprint calculation completely ignores the clock field, which provides backward compatibility with earlier MD5 fingerprints.

The MD5 fingerprint calculated this way is used, for example, to index into the songlength database, because it provides a way to uniquely identify SID tunes even if the textual credit fields of the PSID file were changed.

$OBJECT->alwaysValidateWrite([SCALAR])

If SCALAR is non-zero, $OBJECT->validate() will always be called before $OBJECT->write() actually writes a file to disk. If SCALAR is 0, this won't happen and the stored PSID data will be written to disk virtually untouched - this is also the default behavior.

$OBJECT->validate()

Regardless of how the PSID fields were populated, this operation will update the stored PSID data to comply with the latest PSID version (v2NG). Thus, it changes the PSID version to 2, and it will also change the other fields so that they take on their prefered values. Operations done by this member function include (but are not limited to):

  • bumping up the PSID version to v2NG by setting version to 2,

  • setting the dataOffset to 0x007C,

  • chopping the textual fields of name, author and copyright to their maximum length of 31 characters,

  • changing the initAddress to a valid non-zero value,

  • changing the loadAddress to 0 if it is non-zero (and also prepending the data with the non-zero loadAddress)

  • making sure that loadAddress, initAddress and playAddress are within the 0x0000-0xFFFF range (since the Commodore-64 had only 64KB addressable memory), and setting them to 0 if they aren't,

  • making sure that startPage and pageLength are within the 0x00-0xFF range, and setting them to 0 if they aren't,

  • setting the pageLength to 0 if startPage is 0.

  • making sure that songs is within the range of [1,256], and changing it to 1 if it less than that or to 256 if it is more than that,

  • making sure that startSong is within the range of [1,songs], and changing it to 1 if it is not,

  • setting only the relevant bits in speed, regardless of how many bits were set before, and setting the rest to 0,

  • setting only the recognized bits in flags, namely 'MUSPlayer', 'psidSpecific', 'clock' and 'sidModel' (bits 0-5), and setting the rest to 0,

  • removing extra bytes that may have been between the PSID header and data in the file (usually happens when dataOffset is larger than the total size of the PSID header, i.e. larger than 0x007C),

  • setting the reserved field to 0,

BUGS

None is known to exist at this time. If you find any bugs in this module, report them to the author (see "COPYRIGHT" below).

TO DO LIST

More or less in order of perceived priority, from most urgent to least urgent.

  • Extend the module to be able to handle all kinds of C64 music files (eg. MUS, .INFO, old .SID and .DAT, etc.), not just PSID .SIDs.

  • Overload '=' so two objects can be assigned to each other?

COPYRIGHT

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

PSID Perl module - Copyright (C) 1999, 2002 LaLa <LaLa@C64.org>

(Thanks to Adam Lorentzon for showing me how to extract binary data from PSID files! :-)

PSID MD5 calculation - Copyright (C) 2001 Michael Schwendt <sidplay@geocities.com>

VERSION

Version v2.02, released to CPAN on September 5, 2002.

First version created on June 11, 1999.

SEE ALSO

the SIDPLAY homepage for the PSID file format documentation:

http://www.geocities.com/SiliconValley/Lakes/5147/

the SIDPLAY2 homepage for documents about the PSID v2NG extensions:

http://sidplay2.sourceforge.net

the High Voltage SID Collection, the most comprehensive archive of SID tunes for SID files:

http://www.hvsc.c64.org

Digest::MD5