Monday, August 11, 2014

Proportional scaling of controls


With the anchor points of controls, there’s already a lot of possibilities to layout the user interface. The set of lock properties (LockTop, LockLeft, etc) of a RectControl give control over the scaling behavior of control elements when a Window changes size. In nearly all cases this is enough, but sometimes a proportional scaling of elements is best as the Window or other container control changes size.


Proportional scaling can always be done manually in the Resize events of the Window for every RectControl. To make this a bit easier to maintain, two extra methods and a property can be added to a Window or a Panel to do proportional scaling. A method to record all design time controls locations is called from the Open event of the Window or Panel. Then the method to play back the relative locations is called from the resizing events.


The RecordAllpagepanelControls method iterates through all RectControls on the Window and stores the relative location in a String.

  Dim i As Integer
  Dim ctrl As RectControl
  Dim l,t,w,h As Double
 
  Redim ControlsLocations(-1) //should be empty, in case it wasn't.

  For i = 0 To Self.ControlCount-1
   
    If Self.Control(i) IsA RectControl Then
      ctrl = RectControl( Self.Control(i) )
      l=ctrl.Left   / Self.Width
      t=ctrl.Top    / Self.Height
      w=ctrl.Width  / Self.Width
      h=ctrl.Height / Self.Height
     
      ControlsLocations.Append( Str(l) + ";" +Str(t) + ";" +Str(w) + ";" +Str(h) +";" + ctrl.Name)
    End if
   
  Next

The PlaybackPagepanelControls method reads the locations from the String and updates the positions of all stored controls to the new position relative to the current size of the Window.

  Dim i As Integer
  Dim ii As Integer
  Dim s As String
  Dim ctrl As RectControl
  Dim l,t,w,h As Double
 
  ii = 0
 
  For i = 0 To Self.ControlCount-1
   
    If Self.Control(i) IsA RectControl Then
     
      ctrl = RectControl( Self.Control(i) )
     
      s = ControlsLocations(ii)
     
      l=Val(NthField(s, ";", 1))
      t=Val(NthField(s, ";", 2))
      w=Val(NthField(s, ";", 3))
      h=Val(NthField(s, ";", 4))
     
      ctrl.Left = Round(Self.Width * l)
      ctrl.Top = Round(Self.Height * t)
      ctrl.Width = Round(Self.Width * w)
      ctrl.Height = Round(Self.Height * h)
     
      ii = ii+1 //next in ControlsLocations array
    End if
   
  Next

It would probably be even easier to create a component that performs this scaling or a component that adds this scaling to the container control it’s added to. For more detailed behavior, extra tests on the type of control can be added in the play back function of course.

Adding the methods to a simple example Window with a couple of shapes on it, these now scale proportionally.




Tuesday, May 27, 2014

ID3 Viewer (and Writer) application

A small utility program that uses the ID3 classes posted earlier to view the tags and album artwork of an MP3 file.

Complete source for the program can be downloaded here. Should work in most RealStudio or Xojo versions.

Use the menu to open a file or drop an MP3 file on the window and the program opens the file and shows the main tags. A double-click on the artwork area plays the MP3 file. Clicking on the progress bar that then appears allows to scroll to that position in the file.

The text tags can be edited, the artwork can be changed by dropping an image file on the artwork area. Use the save menu items to write an MP3 file with the updated tags.

The compiled program for a few platforms, in case you don't have RealStudio or Xojo or don't want to bother with compiling:

ID3Viewer for Mac OS X (Intel)
ID3Viewer for Mac OS X (PPC)

Free to use.

Saturday, May 10, 2014

Reading and writing ID3 tags in an MP3 file

One of the things that is fairly easy to make in RealBasic (or Xojo) is of course a small media player. The MoviePlayer component allows you to quickly build a small music player to play MP3 files that fits your own tastes and particular needs. What is a bit harder to do though, is to display the artist and title tags of an MP3 file. And slightly harder still to find a way to display any embedded album art in the ID3 tag. There is a way to get some metadata about a file via quicktime, but this is limited to basic artist and title information.

First thing to read ID3 tags is to know about the various versions of the tags. There is the version 1 (ID3V1) of tagging. That appends a fixed size block of text data to the end of the MP3 file. The more capable and now widespread tag is the ID3 version 2 with its variations 2.2, 2.3 and 2.4 (ID3V2). This is a block with text, image or any binary data of flexible size at the beginning of the file before the MP3 music data. There is quite a bit of documentation on these formats on the web, example source in other languages and of course lots of MP3 files with tags to be looked at with a Hex Editor.

There is source code in RealBasic on the web to read ID3 version 1 tags, but no source for reading the ID3V2 tags. Wanting to be able to read album art and be capable of also reading modern tags with their international characters, I made some classes to do just that for both ID3V1 and ID3V2.

These classes open the MP3 file as a BinaryStream and then parse the file byte-by-byte to see what version tag is present and extract the metadata on title, artist, etc and a Picture for the album art.


The classes are a bit basic in that they only read and keep as properties a limited set of metadata, only the items I was interested in. The classes would on the other hand be not that hard to expand to create a dictionary of all tags in a file and read any and all metadata. Another limitation is that the ID3V2Reader class only returns the first image found in the tag as the album art and ignores any additional images.

The ID3V2Reader does however know about and is robust against tags that were written by an older iTunes version that used base 256 encoded tag sizes instead of the correct base 128 encoded size. (Took a few unexpected results on a few MP3 files to clue on to that one though...) The class should also deal correctly with all the possible text encodings, including UTF16 without a BOM.

Wanting to be able to edit and update tags, also an ID3 tag writing class was made. When writing tags this requires a source file as FolderItem and a destination file (as FolderItem) to write the new file with tags into with the MP3 data from the source file. Note that any metadata of a type not supported by the class is left out and not copied from the source.


As the class structure shows, a few helper encoding and decoding functions are used. Numbers in the tag structure are e.g. encoded so that there will never be a series of 4 zero's, because that is a sync tag for the MP3 decoder (or at least something like that).

As a measure to prevent having to re-write the entire file every time a metadata element is changed, e.g. by adding a single character to the title, a padding area is defined at the end of the tag. This allows for small changes in the size of the tag without having to do a full re-write. The size of this area can be set in the ID3Writer class to a value in bytes.

A zipped archive with the source to all three classes can be downloaded here.

These should work and compile on pretty much any version of RealBasic / Real Studio / Xojo.

Thursday, April 17, 2014

Segments and printing in more than 72dpi

Printing is fairly straightforward in Realbasic (or Xojo if you will). If a call of OpenPrinterDialog returns a Graphics pointer that is not nil, then it is a simple drawing onto that Graphics object. It has the size of the available page, but of course in pixels. Probably to enable being truly cross-platform, the Realbasic printer has a resolution of 72dpi, even though the printer will be capable of much higher resolutions.

Luckily, drawing text with DrawString will draw crisp text in the high resolution of the printer. When drawing graphical shapes however the positioning is limited to the integer pixel coordinates. A widely written about solution to this is to draw the graphics on a Picture (Graphics) with a higher resolution and then use DrawPicture to draw it scaled down onto the printer Graphics. Then it will be drawn in the high printer resolution and you still can draw to actual dimensions. I.e. it is still possible to draw a line that is one inch long on paper.

By defining a magnification factor and then drawing a magnified Picture to be drawn scaled down by that same factor again on the printer's Graphics, more precise graphics is possible and the printer's (default) higher resolution is used. With the magnified Picture (BarsImage) already prepared, the menu handler for the print menu becomes simply:


  Dim h As Graphics
  h = OpenPrinterDialog
  If h<> Nil Then
    h.DrawPicture(BarsImage, 0, 0,BarsImage.Width/MagFactor, BarsImage.Height/MagFactor, 0, 0, BarsImage.Width, BarsImage.Height)
  End if
  Return True
 

This is all implemented in the small utility to draw strobe discs. Strobe discs are used to check or adjust the speed of turntables. There is no shortage of strobe disc images on the internet for download of course. This small utility allows fairly easy printing of a strobe disc with the settings you want. Source for the Strobe Maker project is downloadable, so you can tweak the design as desired.
Another item encountered was drawing speed. When drawing this particular graphics, the first obvious method tried was to use the ArcShape object and then draw a series of these rotated. (Drawing a white circle over the centre to make them bars.) This was very crisp and accurate, but really slow. The drawing of the whole shape with ~70 arc segments was slow; drawing 216 arc segments made the program freeze and think about that for almost a minute.

Oddly and fortunately drawing a FigureShape is much faster. Calculating the 4 points of the ArcShape (and accepting that the ends are straight lines) these FigureShape draws the 216 segments in just under a second on the same machine. Then drawing a white bounding circle even adds the rounded segment ends then.