Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Lyrics Archiver

5.00/5 (4 votes)
10 Nov 2020CPOL6 min read 7.2K   161  
Windows Forms .NET Core app enables to show, search, edit and store lyrics within 7z-archive
This application is part of a greater project that I'm going to post soon. It stores the texts in a SevenZipSharp library and enables you to see, edit, add, delete and search text fragments (e.g. song lyrics) within a .NET Core Windows Form Controls.

Image 1

Introduction

If you are a serious, grave-crucial app developer, then grab the code and skip this introduction.
When I was 11 years old, my aunt made me a gift of a new, shiny, German accordion (at that time, my uncle served as an officer in East Germany). People at a nearby musical school proposed free lessons only if we substituted this nice accordion by Russian version ('bayan'). My parents did it and I learned harmony and instrumental skills for almost 2 years. By that time, "western" winds and some of my friends whispered in my ear that playing folk's instruments are not so cool. A much better choice would be a piano, sax or drums. So I stopped practicing. Little did I know about a great impact of musical practicing on general brain development. The proof is my grandson, Ivan. My daughter (his mother) bought him a violin when he was 2.5 years old (unbelievable!).

So by the age of 15, I was surprised when I realized that I loved jazz. At first vocal (Peggy Lee, Ella Fitzgerald, Sarah Vaughan...), then instrumental (Cannonbal Adderley, Charley Parker, Miles Davis...). My friends totally could not perceive or tolerate instrumental jazz (except some Louis Prima's or Ray Charles's rare improvisations). You either ardently love jazz or you just hate it, except some sweet artists like Fausto Papetti, Chris Botti, etc. which are admired or just tolerated.

Listening to a jazz song and not being able to understand its lyrics is a real torment that provokes learning. I believe that the best way to learn English is listening to various jazz songs and analysing the lyrics.

This Project Features

The hereby application Lyrics is a part of a greater project which I'm going to post soon. Current Windows Forms .NET Core application allows user to store texts in a SevenZipSharp archive, read these text fragments, edit, add, delete them and search for a user specified text fragment.

The next WpfPlayer project (not the current one) enables a user to work with a storage consisting of musical (mp3, flac, ogg), text and images files. It allows to:

  • select a performer
  • select his/her album
  • listen to the track selected
  • search the Web for a selected artist, read this artist's info within an embedded WebBrowser control
  • read current track/album info: critics review or personnel or lyrics (if it's found in a Lyrics.7z file)
  • watch album and artist's images (timer-operated)

All these things are shown in different WPF-controls of the main window. It is assumed that beforehand user has pointed to the folder where his musical (and other) files are located.

Coding Details

Back to the current project. Titles of the archived songs are shown in DataGridView grid. This control proves to be more flexible than ListBox. Lyrics are shown in a PaddedRichTextBox control. I borrowed its DLL from Tony Zackin's article here on CodeProject. I greatly appreciate the contribution of persons who dare to override WndProc and work with low-level entities, such as Windows messages (WM_NCCALCSIZE, etc.).

Long ago for several years, I messed around with (first Soviet machine) low-level coding. It was even one level below the machine-code-level. Those entities were called elementary operations, like RegARegB (transfer value from register A to register B). But soon, some stupid politicians surrendered in the cold war and ruined all the industry of native computer building. In the middle of the 70s, we had only the ES EVM machines (Soviet IBM prototypes). Genuine Soviet computing machines (like BESM-6 or several others) were dying at that time. Now I can clearly imagine the level of qualification and amount of work needed to work with low-level codes. These people may override any problem.

Most efforts were spent at implementing the Search facility. Circular searching is made as within titles and within lyrics texts. The following method implements the circular search algorithm. I believe the code is self-explained. Here, two class variables are used: titleFound (a boolean flag), what — text fragment to look for.

C#
void Search()
{
  while (true)                  // For all the titles
  {
    var row = GetNextRow();     // Song title is here
    if (row == null)
    {
      new FormMsg("Reached the end of collection", 2000); // Close after 2 seconds
      searchRowId = -1;
      return;
    }
    if (titleFound)             // Class variable
    {
      SelectRow(row);
      return;
    }
    else
    {
      while (true)              // Search through all lyrics texts 
      {
        var o = row.Cells[0].Value;
        string s = File.ReadAllText($"{pathLyric}{o}.txt");
        if (FindPos(s, 0) != -1 || titleFound)
        {
          SelectRow(row);
          return;
        }
        row = GetNextRow();
        if (row == null)
          break;
      }
    }
  }
}

DataGridViewRow GetNextRow()
{
  int
    count = grid.Rows.Count - 1,
    next = searchRowId + 1;
  if (next > count - 1)        // Consider that last row in a grid is always empty, 
                               // and counting from zero.
    return null;
  searchRowId = next % count;  // circular search
  var row = grid.Rows[searchRowId];
  var o = row.Cells[0].Value;
  titleFound = $"{o}".Contains(what, StringComparison.InvariantCultureIgnoreCase);
  return row;
}

int FindPos(string s, int start)
{
  return s.IndexOf(what, start, StringComparison.InvariantCultureIgnoreCase);
}

The last row in a grid (which is always present and empty) is used for adding new titles. Most of the tasks (beside searching) are placed within event handling methods. I mean user generated events such as: grid.SelectionChanged, grid.UserDeletingRow, and RichTextBox.Leave (focus). For instance, analyse the logic of handling grid.UserDeletingRow event:

C#
void Grid_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
{
  var o = e.Row.Cells[0].Value;
  if (o != null)
  {
    var fn = $"{pathLyric}{o}.txt";
    var dlg = new FormMsgBox(this, $"Confirm Deleting Song: '{o}'") { Visible = false };
    if (dlg.ShowDialog() == DialogResult.Yes)
    {
      File.Delete(fn);
      UpdateCount();
    }
    else
      e.Cancel = true;
  }
}

Two separate forms: FormMsg and FormMsgBox were created to override the dull-looking Microsoft MessageBox. For simple forms, there is no need to use Visual Studio Designer. The code is much more informative and easy to read than the code auto-generated by the Studio. Here is an example of the FormMsg form that presents a message for a given period of time and then disappears. But clicking the left mouse button, the user may hold the message (as long as needed) and carefully read its (especially lengthy) text. By clicking right button, the user may place the message into the Clipboard object. Notice, the code doesn't need Visual Studio Designer facility and Resources files.

C#
public class FormMsg : Form
{
  bool stay;
  public FormMsg(string msg, int interval)
  {
    var text = Kit.FormatMsg(msg);
    var timer = new Timer { Interval = interval };
    timer.Tick += (s, e) =>
    {
      if (!stay)
      Dispose();
    };

    var lbl = new Label
    {
      Font = new Font("Trebuchet MS", 11.0F),
      ForeColor = Color.Yellow,
      Padding = new Padding(16),
      Text = text.ToString(),
      AutoSize = true
    };
    lbl.Paint += (s, e) => { e.Graphics.DrawRectangle
                 (Pens.Yellow, 5, 5, lbl.Width - 7, lbl.Height - 10); };
    lbl.MouseDown += (s, e) =>
    {
      if (e.Button == MouseButtons.Right)
      {
        Clipboard.Clear();
        Clipboard.SetText(lbl.Text);
      }
      stay = !stay;
      if (!stay)
        Dispose();
    };
    Controls.Add(lbl);

    BackColor = Color.Black;
    FormBorderStyle = FormBorderStyle.None;
    AutoSize = true;
    AutoSizeMode = AutoSizeMode.GrowAndShrink;
    TopMost = true;
    Load += (s, e) =>
    {
      var form = Application.OpenForms[0];
      Left = form.Left + 230;
      Top = form.Top + 200;
      timer.Start();
    };

    Show();
    Update();
  }
}

To rename files (in C#), Microsoft provides a File.Move method which looks (and works) rather queer. I find that the better way is using InteropServices and rename function from stdio.h.

Using the Code

When you find your favorite lyrics and are ready to store it in your archive (Lyrics.7z by default):

  • Press Ctrl+End to bring into view the last empty grid row.
  • Enter the song title and press Enter — temporary file named title.txt will be created.
  • Press Up arrow key to see the dummy text of the file.
  • Select the dummy text in the RichTextBox.
  • Press Ctrl+V to paste the lyrics and to edit it within PaddedRichTextBox (if needed).

When focus leaves PaddedRichTextBox, your current lyrics text will be saved in a temporary file (later, it will be archived in Lyrics.7z). This will happen automatically when you exit the app or when you click Save button on the ToolStrip. All the temporary files within Lyrics/Data/Tmp folder also will be deleted when you close the form. Your archive is updated and ready to be used by some more sophisticated application. I am going to post such an app WpfPlayer after I polish it enough to satisfy CodeProject requirements.

Public static class Kit is used to hold all the useful global functions (or static methods in C#) which any developer collects during his whole life. Here, the class holds only four methods which are used to reformat, strip, trim text fragments and search for folders with data and library files.

That's all, folks! Thank you for reading.

History

  • 2nd September, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)