REBOL
Docs Blog Get-it

REBOL Quick Start: Part 5 - Files, Directories, and Playing Music

By Carl Sassenrath
REBOL's Designer
Revised: 1-Feb-2010

The Plan

The prior tutorials were educational. Time to do something more interesting.

So far, you've seen how to view and edit existing REBOL source code, how to write your own scripts, and how to help yourself when problems occur. This is useful info to know.

In this chapter, I will show you how to scan your system for MP3 and other files, then play them on your media player. (I will show you this for is Windows Media Player, but you can modify this example to use whatever you want).

REBOL/View 1.3 Needed

Be sure to use REBOL/View 1.3 or newer (in order to run the media player using the call function).

File Notation

REBOL uses a special notation for files and directories. For example:

do %demo.r
print read %readme.txt
data: load %business/contacts.r
dir: %"/c/documents and settings/Carl/"
dir: %/c/documents%20and%20settings/Carl/

Here's what to notice:

  1. Files begin with a % (percent sign). This distinguishes them from all other types of strings. Why? Because when you (or a script) scan a file or other REBOL data, you can immediately recognize what values are files.
  2. Directory paths use the / (forward slash) just like Web URLs. This has been the international standard for 40 years. (Unfortunately, Microsoft designers didn't know that and used the \ by accident. But, we allow that in REBOL too.)
  3. The file notation is the same on all computer systems. That's why REBOL does not use the Windows format. You want your scripts to work on OSX, Linux, your cell phone too... everywhere.
  4. Use quotes if the file has spaces. You can also use the URL format (percent-hex encoded).
  5. A directory is just another name for "folder". Yep, you knew that already.

Listing File Directories

To get a list of files within a directory, you can do this:

files: load %music/
probe files
[%Sultans%20of%20Swing.wma %Twisting%20by%20the%20Pool.wma ...]

Notice that slash on the end of the %music/ directory. We use that to show that it is a directory. Later, we will show why that is important.

For this to work, the music directory must be found in the same location as where you run REBOL. We call that location the "current directory". To list files from other locations, you can use an absolute path such as:

files: load %"/c/documents and settings/Carl/"
probe files

or, a relative path (go up two directories in this case):

files: load %../../music/
probe files

or, or you can change the current directory:

change-dir %"/c/documents and settings/carl/"
files: load %music/
probe files

On this last one take note: any file you now read or write will use this directory location as the default.

A Note on File Security

This is Important

Read this section. You need to know this. Do not skip it.

If you try the above examples, REBOL will pop up a security alert. It does that to protect you from scripts that could read, write, or delete your files.

The default security is: don't let scripts mess with my files other than those found in the REBOL directories. That is the safe thing to do.

When the REBOL security requestor pops up, only answer YES or ALL if you trust the script you are running.

Most scripts are safe, but you can never know that for sure, so be careful. If necessary, look at the source for the script or ask other REBOLers for advice.

Of course, if you are running your own scripts from your local disk, you can safely do that. In fact, you can put the -s option in your REBOL command line (or in your editor command that runs REBOL) to disable the security for your own scripts.

For more information, read more about REBOL's the secure function. It offers a range of file control and sandbox features.

Build A List of Files

You learned above that you can get a directory of files with:

files: load %music/
probe files
[%Sultans%20of%20Swing.wma %Twisting%20by%20the%20Pool.wma ...]

Now let's say you want only specific files, such as mp3 files. You can write something simple like:

file-list: []
foreach file load %music/ [
    if %.mp3 = suffix? file [append file-list file]
]
probe file-list

Note that we write:

%.mp3 = suffix? file

and not:

(suffix? file) = %.mp3

Note the parens. Those are necessary if you write it this way! A lot of us don't like typing parens, so we simply reverse the expression to avoid them. But, either way works just fine.

A Variety of Files

Above, we just get mp3 files, but what if we also want to include a few other types. Here's what you write:

file-types: [%.mp3 %.wma %.wav]
file-list: []
foreach file load %music/ [
    if find file-types suffix? file [append file-list file]
]
probe file-list

The file types are just a block (a list) of file suffixes for music. This is a handy method. For each file we check if the suffix is in our file-types list.

Go ahead and add other music file types as needed.

Reusable Code

You can reuse this code to find all .txt, .r, or any other type of file. Keep a copy of it handy.

Sorting Files

For some reason the Windows OS does not list directory files in alphabetical order. This comes from the MSDOS days, but it just seems plain wrong these days.

No problem, you can fix that by changing the line above to:

foreach file sort load %music/ [

Now the files will be sorted alphabetically for your search.

Adding Sub-directories

Let's expand the above code to get all matching files from all sub-directories (directories that are in our directory). The easiest way to do that is to create a function such as:

find-files: func [dir list /local files] [

    files: sort load dir

    ; Get files that match the above types:
    foreach file files [
        if find file-types suffix? file [
            append list dir/:file
        ]
    ]

    ; Search sub-directories:
    foreach file files [
        if find file "/" [find-files dir/:file list]
    ]
]

We can try it out with:

file-types: [%.mp3 %.wma %.wav]
file-list: []
find-files %music/ file-list
probe file-list

or, for a long list you can do:

foreach file file-list [print file]

A few notes:

  1. This function calls itself to get the next directory. It is recursive. This is a very useful technique that you will want to remember.
  2. I go through the file list twice. The first time to collect the music files; the second to scan of the subdirectories.
  3. I use a special path notation dir/:file to join the directory and file name. You can also use join but I wanted to introduce this special method. Don't forget the ":" in that code. It's how you get the value of the file variable, not just the word file.
Function Naming

REBOL begins function names with verbs. You may have noticed. We say make, copy, find, etc.

This is a useful naming convention, and I encourage you to use it. After 30 years of programming, I find it really helps make my programs more readable.

That's why I called the above function find-files and the list of files is just file-list.'''

Playing A Song

In Windows, there are various ways to play a song. Here, I will use the Windows Media Player (WMP). I'm going to show you a general method for calling other types of programs, not just the Windows media player.

Find the icon for WMP and right click it to find the path to the player. On my system it is:

"C:\Program Files\Windows Media Player\wmplayer.exe"

but, Windows will put in other places too.

To launch the player, you can simply write:

call "C:\Program Files\Windows Media Player\wmplayer.exe"

It should open on your screen. If not, then verify the path is correct.

To make this easier, we're going to use a variable for the WMP path:

player: "C:\Program Files\Windows Media Player\wmplayer.exe"
call player

Now, to play a song, you can provide the file for it, and form a command line with it. Here's the code:

song: %rebol-song.mp3
call reform [player song]

It's really important to know how to debug this line because if it does not work, you want to figure out why. So, to see what is being called, just write:

song: %rebol-song.mp3
call probe reform [player song]

There's that handy probe function I mentioned before. You will use it a lot.

Now, to play the first song from our file list above, you write:

file-types: [%.mp3 %.wma %.wav]
file-list: []
find-files %music/ file-list
call probe reform [player first song]

Did it work? If not, you will want to read the next section.

Local File Format

As you learned above, in REBOL you write files in a way that works on all computer systems. For example, you write:

file: %"/c/documents and settings/Carl/song.mp3"

But, Windows programs do not like that format. They want something more like:

file: "c:\documents and settings\Carl\song.mp3"

So you must convert the file path. We make that easy with:

wfile: to-local-file file

The to-local-file will convert the file to the correct format for the local system (Windows, OSX, Linux, etc.).

When you use functions like call (that access programs on the local system) you must use the local file format.

But, of course, there's more...

  1. If a file has spaces, you may need to add quotes around it before you use it in call. It works just like the Windows cmd shell.
  2. Some programs want the full file path (the absolute path, including the drive name).

So, we will create a new function localize-file to do all of this:

localize-file: func [file] [
    rejoin [{"} to-local-file clean-path file {"}]
]

The clean-path function joins the current directory to your file name to create a full file path. We then convert that to local format and add the quotes.

Play a User-Selected Song

To illustrate the above, here's how to make your script play a song that you select from a directory listing:

file: request-file/only
if not file [quit]
call probe reform [player localize-file file]

The request-file opens a file requestor. The /only refinement tells it to return only a single file (not a list of files).

Note that if the user clicked "cancel" then the program will quit. It is always a good idea to do that.

Play a Pile of Songs

Now, let's tie it all together. Here is the complete script. And, yes, I've tested it, and it works fine here.

I use comments to break the script into sections:

REBOL [Title: "Play a pile of songs"]

;--- 1. Setup (change this as needed)

music-dir: %/c/music/

file-types: [%.wma %.mp3 %.wav]

player: "C:\Program Files\Windows Media Player\wmplayer.exe"

;--- 2. Function that gathers all the files:

file-list: []  ; hold list of media files (rebol format)

find-files: func [dir list /local files] [

    files: sort load dir

    ; Get files that match the above types:
    foreach file files [
        if find file-types suffix? file [
            append list dir/:file
        ]
    ]

    ; Search sub-directories:
    foreach file files [
        if find file "/" [find-files dir/:file list]
    ]
]

find-files music-dir file-list

;--- 3. Convert the file list to local format:

localize-file: func [file] [
    rejoin [{"} to-local-file clean-path file {"}]
]

play-list: []  ; hold list of media files (windows format)
foreach file file-list [
    append play-list localize-file file
]

;--- 3. Play the list:

call probe reform [player play-list]

halt

When the script works properly, you can remove the probe function.

If you just want to play the first four songs, you can write:

call probe reform [player copy/part play-list 4]

That copies just part of the list and passes it to the player.

User Selected Directory

Finally, to select the directory to search for songs, you can make this little change:

music-dir: request-dir
if not music-dir [quit]

If the user clicked "cancel" then the program will quit.

Next: Fun Graphics

This tutorial above talked about how to open the console and get help.

In the next tutorial, we'll show you how to create a graphical user interface (GUI) form to submit data to a server.

About | Contact | PrivacyREBOL Technologies 2024