Friday, October 24, 2014

Lloyd's Video Analysis Tool, Part 8: Prototype Ready for Some User Testing


I've been working slowly but surely on my video analysis tool. I've now finished the initial programming of all of the essential features, including the transcription tool. I've also spent some time on the graphic design of the project, which, while not great, now makes the tool somewhat more "presentable." I feel I now have a prototype that is ready to be shared and tested by some people who have been expressing interest in the tool. Getting feedback from people who are already analyzing video in their work, but who are also willing to put up with yet-to-be-discovered bugs and problems will be crucial.

I have prepared a short video to explain how the tool works. I intentionally did not mention LiveCode at all because I know the people with whom I'll share this video care deeply about video analysis, but don't care at all about how the tool was made.



Here's a list of the main features of the tool:
  • Defining as many video scenes as you wish in the video file you are analyzing (I just call them video entries).
  • Tagging the video scenes with as many tags as you wish.
  • Creating a list of "quick tags" to more easily add the most common tags or codes to a video scene.
  • Adding a comment or description to each video scene.
  • Adding a transcription to each video scene, along with a transcription tool inspired by the YouTube transcription tool.
  • The option to create reports of a video analysis project, including an "Excel ready" report option.
  • A file management system that saves all the projects inside a special folder in the computer's document folder.
  • The option to manually save (or export) back-ups of a video project (called "source files").
  • The option to import source files; this is meant as a way to share the project with another user of the tool. (However, you also have to share the video file separately.)
I'm particularly proud of the transcription tool. Although it still needs some work, I find it very effective way to create transcriptions because the tool directly controls the video as you type. When the tool is activated, only the space bar plays the video and every other key stops the video. There is also an automatic rewinding effect. I also included a 5 second manual rewind option.

There are still a few features I still would like to add to the tool. The one at the top of the list is a search option, mainly for searching among the tags.

All of my testing so far has been on the Macintosh, but I will also begin testing a Windows version of the tool.

I want to publicly thank a few people who made some very helpful suggestions based on demonstrations of earlier prototypes back in July and August: Dr. AnnaMarie Conner, Dr. TJ Kopcha, and Dr. Mardi Schmeichel. Dr. Conner is on the faculty in the Department of Mathematics and Science Education, Dr. Kopcha is fellow member of the Learning, Design, and Technology faculty, and Dr. Schmeichel is on the faculty in the Department of Educational Theory and Practice.

Special thanks to AnnaMarie and Mardi for taking some extra time to show me the qualitative analysis tools they are using for video. Mardi uses Dedoose and AnnaMarie uses Transana. AnnaMarie's research involves very complex and expansive use of video.

One of the most beneficial things about seeing a tool like Transana, especially within the context of a sophisticated video-based research program, is that it reinforces and clarifies my initial thoughts that my tool is not meant in any way to compete with them. My video analysis tool is meant for small scale, niche applications, only some of which will be research.

Finally, special thanks also go to Daisyane Barreto, a doctoral student in our department who has been testing an earlier prototype of my tool for the past month or so in the analysis of video data she has collected for her dissertation research. She reports that the tool is working well for her and I made some modifications to the current prototype based on my conversations with her.



Tuesday, September 16, 2014

Reflections on the RunRevLive.14 Conference

I recently attended and presented at the RunRevLive.14 conference in San Diego, CA. As a result, I have come to two simple conclusions:
  1. People at RunRev and the LiveCode community at large consist of very, very smart people who are also very helpful, supportive, and kind.
  2. I know about 1% of LiveCode.
Even though I do feel now as if I know very little of what LiveCode can do, the fact that I am still able to do so much shows its power and potential. Consequently, the conference has greatly reinforced my decision to commit to learning LiveCode.

I was able to attend the conference because my proposal to make a presentation at the conference was accepted. This came with a complimentary conference registration, otherwise, I never could have afforded the $1599 registration fee. The title of my presentation was "From Concept to Prototype to the App Store" and it featured the story of my design of my iOS app "Lunar Hotel Shuttle." I've written about this app in previous posts, but in short, the app teaches about Newton’s Laws of Motion in a futuristic, imaginary context. The storyline is that NASA has built a hotel on the moon that is so large that elevators are not possible or practical. Instead, hotel guests are shuttled to their hotel floor by a small shuttle that the player has to learn how to fly.

I explained in my presentation that the main purpose of developing this app was not to create a great game that would make any money, but rather to test the appropriateness and functionality of LiveCode for action games on mobile devices, especially a game where the "feel" is very important. This was important to me in order to know whether LiveCode would be an appropriate tool for projects like this and to understand where LiveCode's limits were to be found for games requiring high responsiveness. I also wanted to test out a wide range of functionality within LiveCode.

I also contrasted LiveCode as a development tool for instructional design with Adobe Captivate and Articulate Storyline, probably the two most popular eLearning development tools currently on the market. I used a taxonomy of learner interactions from the military to based my comparison of LiveCode with these other tools:



Levels of Interactivity in eLearning

Level 1 - Passive
Watching a video, self-running slide show, or animation

Level 2 - Limited Participation
Simple interactions (e.g. M/C) with no branching

Level 3 - Complex Participation
Simple and complex interactions (e.g. drag and drop) that conditionally branch the lesson

Level 4 - Real-Time Participation
Games and simulations

Adapted from “Department of Defense Handbook: Development of Interactive Multimedia Instruction,” 1999, Report No. MIL-HDBK-29612-3



I like to refer to Level 4 as "creative interactions." Here were my simple conclusions based on this comparison:
  • LiveCode is an important, but not exclusive, tool in the instructional design & development toolbox.
  • LiveCode is excellent for creating Level 4 (Creative) Interactions.
  • LiveCode is strong for mobile native app development for eLearning, but weak for Web-based eLearning.
  • The option to export to HTML5 should make LiveCode a game changer for eLearning development.
Yes, you read right - RunRev is currently working on providing the option to export LiveCode applications to HTML5. I believe LiveCode will become a very serious contender in the eLearning design world when this export option becomes available. (Apparently, LiveCode will be converted into JavaScript for easy integration into existing HTML5 applications, therefore no plug-ins or Java will be required for it to run.)

My audience was small, but very engaged. I thank all who came to my session. A special thank you to Bernie Dodge, Professor of Learning Design and Technology at San Diego State University, for coming to my session. LiveCode is one of the tools Bernie is recommending to students in his gaming and simulation course this semester. Bernie and I also had some very good conversations at the conference about design and reflections on the recent history (past 30 years or so) of the instructional technology field.

On the plane ride to and from San Diego, I read a good deal from Paul Graham's book Hackers and painters: Big ideas from the computer age, published by O'Reilly Media, Inc. One quote from the book captures well the enthusiasm and commitment of the LiveCode community:
“Ordinary technology changes fast. But programming languages are different: programming languages are not just technology, but what programmers think in. They’re half technology and half religion.” p. 179
I am already looking forward to my next RunRevLive conference.




Sunday, July 13, 2014

Using LiveCode to Stitch Together "Big Data": Yet Another Example of the Value of Craft Programming

I've been teaching a massive open online course (MOOC) for the past year - Statistics in Education for Mere Mortals - and I've been collecting research data all along. Time has come to work on writing up this research to share with my professional community. I tend to work best best when there is a deadline. "Lucky" for me, the deadline for proposals to the 2015 annual conference of the American Educational Research Association (AERA) in Chicago is just around the corner - July 22, 2014.

However, I've been slow in analyzing the data because I've been a little confounded as to how, exactly, to go about it. You see, the data have been collected by the learning management system (LMS), but not in a way that is conducive for research. In order to effectively analyze quantitative data in a research study, you need to have all of the raw data for each participant neatly organized into a single line of information. By raw data, I mean every single detail, such as individual answers to specific questions, not just totals. Put into spreadsheet terms, each person needs to be a row and all of the data for that person needs to be the columns. Then, I can import this into a statistical software tool, such as SPSS.

Collecting Data in a Learning Management System


Unfortunately, most LMSs do not organize course data in this way. It's true the LMS has a grade book that automatically keeps track of all course participants, but this only keeps track of aggregate data, such as subscores on individual quizzes, surveys, and the like. Again, I need the raw data. The LMS does keep track of the raw data, but only within individual activities. A good example for my needs is a series of surveys I ask participants to complete at the beginning, middle, and end of the course with questions such as the following:

Which of the following best describes your learning goal at this moment?

  • I intend to fully participate in the course and complete it.
  • I intend to fully participate in most of the course.
  • I intend mainly to browse and participate in a few activities.
  • I will probably just browse around with little actual participation.
  • I really don't have a specific learning goal at this point.

I don't care about their overall "score" on these surveys, instead I need to focus on answers to specific questions. For example, I'm interested in comparing among those who actually did finish the course with those who declared at the start of the course their intent to do so versus those who said they were there just to browse with no intention to actually complete the course.

So, yes, I have access to raw data for each of these surveys, but these data are "trapped" within individual survey reports. No problem, you say, just copy and paste the data from each survey for each person into a master spreadsheet file. That would work fine if I only had 20 or 30 participants. But, I have had an average of over 800 participants in each of the four courses -- about 3400 overall! And, to complicate things further, participants vary tremendously in terms of what surveys and survey questions they choose to complete. So, I find overwhelming the magnitude and complexity of manually copying and pasting this amount of data together.

I had held out some hope that the LMS administrators could help me out and produce a master spreadsheet file for me, so I made a special request for this. Unfortunately, they reported being unable to comply as apparently the data are also located in separate files at the system level (which is a little surprising).

LiveCode to the Rescue


So, what to do? Fortunately, LiveCode's powerful list processing capabilities once again provided a solution. Most of the data reports contain a few common categories, such as the person's name and a course ID. I was able to use these common data points to "stitch" together the various data files.

The code is surprisingly simple, and is based on a algorithm I've used in many programs I've written about already. Before I get into that, let's see how I've structured the LiveCode stack. The stack consists of just one card. Here's a screen shot:



As you can see, there are three fields named (from left to right) "data1," "data2," and "finaldata." The idea is to copy and paste comma-delimited text of the data from an individual survey into the first two fields, with the first course survey going into data1. (The LMS is able to generate Excel files for each survey, so it is then easy to generate a .csv file from that.) Each line is an individual participant with each data point for that person separated with commas. My script starts with the first line in data1, then methodically scans each line in data2 to look for that same person. It if finds a match on the person's name, it then combines (i.e. "stitches") the two lines together and puts it into the next line of field "finaldata." If it doesn't find a match, it then just puts the line from data1 into finaldata.

A Very Short Script That Does Some Heavy-Duty Work!


The script for button "Cross-check with item 1" does this work:

on mouseUp
   put the number of lines in field "data1" into line 1 of field "data1lines"
   put the number of lines in field "data2" into line 1 of field "data2lines"
   
   put empty into field "finaldata"
   
   repeat with i=1 to the number of lines in field "data1"
      put false into varMatch
      put i into line 1 of field "progress"
      repeat with j=1 to the number of lines in field "data2"
         if item 1 of line i of field "data1" = item 1 of line j of field "data2" then
            put true into varMatch
            put line i of field "data1"&comma&line j of field "data2"&return after field "finaldata"
            exit repeat
         end if
      end repeat
      if varMatch = false then put line i of field "data1"&return after field "finaldata"
   end repeat
   
   beep 2
end mouseUp


Item 1 of field data1 and data2 contains the person's name. In the parlance of databases, this acts as the "primary key" for each of these two separate data files. The local variable "varMatch" keeps track of whether a match has been found or not for each line in data1. When a match is found, it becomes "true" and the lines from the two fields are combined. If, after scanning all lines in data2, no match is found, this remains false and the line from data1 is added to finaldata without anything added to it.

It's important to note that after I have all the data combined, I need to delete the individual student names. That is important in order to comply with all human subjects policies and procedures. 

Cascading Data, or Creating a Data Quilt


After I run this program once, I am far from done. I have several more surveys and other data files to stitch together in this master file. So, I built the option to first empty fields data1 and data2, then take the data from the finaldata field and move it into data1. Then, I can copy and paste the comma-delimited text file from the next survey into data2 and begin to stitch those together. This reminds me of a cascading waterfall of data. Another way of putting this is to say that I'm stitching together a large "quilt"from each patch of data from the individual surveys!

The Continued Value of Craft Programming


I consider this tool another example of craft programming. The program focuses on solving a unique and narrow problem. Without modifications, it is of benefit to no one else but me. But, it solved a very difficult problem for me very well and was well worth the time to create. It only took me about two hours to figure out how to build this stack. It then took about 30 minutes to run the program for the data for one of my MOOC course sections, stopping as I went to generate a new .csv file for each successive survey followed by copying and pasting the data into the data2 field. I hesitate to estimate what it would have taken to manually cut and paste the data to do the same job. Maybe 25 very tedious hours per MOOC? Maybe a lot more. Really hard to say. And, remember, I have a total of four course sections to analyze, with more sections to be offered next year. I'm also sure I would have made many errors doing this manually, whereas I can trust LiveCode to follow my algorithm perfectly.

There was no one else I could turn to for help on this. No "real" programmer within my university would have been inclined to take on this job as a service to a researcher like me and I had no funds to pay someone to do it. Creating this craft programming project with LiveCode has been a lifesaver.

Now on to the real work of analyzing the data.

Wednesday, July 2, 2014

Lloyd's Video Analysis Tool, Part 7: Generating Reports

I added the feature of generating some simple reports to the project. I am deliberating not including a video in this posting because I will reporting in my next post on my first public demonstration of the project prototype to my college held earlier today. That posting will include a short video introducing the current prototype from start to finish. I'll include a good overview of the the reporting features.

Two Report Options


As I've written elsewhere in this blog, I'm a big fan of using Excel whenever possible to help organize and analyze data from projects. So, I created an "Excel Ready" report option that creates and saves a comma-separated value (.csv) file to the user's hard drive. Here is an example of this "table style" report already opened in Excel:




From here, one can use Excel to sort, organize, or search the data.

I also created a second report option that produces a simple text document using a "label style" format. Here's an example already opened in the Mac's TextEdit application:



This could obviously be opened instead with Microsoft Word and then edited anyway the user wishes.

Other Report Options to Consider


There is obviously much more that can be done here. For example, I plan on providing the option to produce reports that only focus on certain tags or combinations of tags. Likewise, I plan on giving users the option to include or exclude categories of information. For example, the user may just want the comments, but not the tags or the time code.

Tag Summary Report


At the bottom of both reports, you can see I added a short "Tag Summary Report." This seemed like a very obvious and useful summary of some key information. The list processing strengths of LiveCode make these sorts of summaries easy to produce. I again use the strategy of having two "shadow fields" (fields the user can't see) in combination with two scripts:



The first script scans the entire video analysis data and builds a running log of all tags in the field "tags found." After this field is generated, a second script scans it to produce the field "unique tags" on the right. The contents of this second field is then copied to the bottom of the report using yet another field not shown here titled "report bin."

Here's the code for producing the Excel-ready version (the text only version only differs in the way it outputs the data):

//Create list of unique tags
   repeat with i = 1 to the number of lines in field "tags found"
      put false into varTagFound
      repeat with i2 = 1 to the number of lines in field "unique tags"
         if line i of field "tags found" = line i2 of field "unique tags" then 
            put true into varTagFound
            exit repeat
         end if
      end repeat
      if varTagFound = false then put line i of field "tags found" into line (the number of lines in field "unique tags"+1) of field "unique tags"
   end repeat
   
   //Count the use of each unique tag
   repeat with i = 1 to the number of lines in field "unique tags"
      put 0 into varCountTags
      repeat with j = 1 to the number of lines in field "tags found"
         if line i of field "unique tags" = line j of field "tags found" then add 1 to varCountTags
      end repeat
      put varCountTags into item 2 of line i of field "unique tags"
   end repeat
   
   put the number of lines in field "report bin" into l
   put "Tags used in this analysis:"&comma&"Frequency"&return into line l+2 of field "report bin"
   put field "unique tags" after field "report bin"
   
Yes, there is a lot of nested repeating going on here! But LiveCode somehow makes it easy to conceptualize these repeat structures in ways that other programming languages don't, at least for me.

Final Thoughts


These simple reports were the last major piece of the puzzle to creating a tool that would actually do something useful. Even if I stopped working on the project right now, I have a tool (albeit crude) that does some useful work. This is a very important milestone. Heck, I might almost be ready to declare that the project has moved from beta version 2 to beta version 3!


Sunday, June 22, 2014

Lloyd's Video Analysis Tool, Part 6: File Management

I have a brother-in-law in the construction business. He talks about working with clients who get very happy and excited when they see wall studs go up or an old roof being rapidly torn off.  In contrast, they can get impatient when he is working on plumbing or electrical jobs because they don't see much visual progress, or VP. This is frustrating to him because he is working just as hard and putting in just as many long hours. It's just that you can't see at a glance the progress he's made. Well, I've been working on some of the "plumbing" for my video analysis tool project lately and though I've been working hard on important stuff, there's not much VP.

Up to this point, I've been using the same movie for all of my project development - my mash-up video of my MOOC on statistics. The movie file was "hard wired" into the project, that is, the file name and path were manually entered and saved into the video player tool. This meant that this movie, and only this movie, would play. So, I've finally updated the project (now up to version 0.2.3) to allow any movie to be used and -- drum roll please -- to allow multiple projects to be saved and edited. This is like having hot and cold water on the construction site! Here is a brief movie demonstrating this latest version:


Obviously, I continue to be largely ignoring the graphic design of the tool. However, I think the user interface design works pretty well.

Saving Project Files


Although I have worked with scripts that save data in other LiveCode projects, I have not worked on a LiveCode project where one has the option to create and then subsequently edit new project files. Before moving forward, I had to decide on the design for this capability. I saw two possibilities. The first is what I consider the standard approach, common in applications such as Word or PowerPoint, where the user creates a new file, then saves it wherever they wish on their computer. It is up to the user to remember where the file is kept (though the "Open Recent" options help the user to quickly recall and find the most recently worked on files). The second approach is more common in mobile apps where the app itself manages all of the stored files. Evernote is a good example of this approach. I decided to go with this second approach, at least for now, particularly because I envision this tool being used on an iPad.

Project Workflow


The ability to create and then work on as many projects as one wishes required a major new design feature related to project workflow. I envisioned three possibilities:
  1. Continue working on the same project as before;
  2. Start a new project;
  3. Select a previously saved project to work on.
I created a new card, which is now the first (or home) card of the stack, to provide these options:


Managing the Project Files


Actually, all of the project files are just text files. For now, I save all these project files into the "Documents" folder of the computer. Macintosh, Windows, iOS, and Android operating systems all have a special documents folder that can easily be accessed using this LiveCode command:

set the defaultFolder to specialFolderPath("Documents")

The defaultFolder property specifies the folder that all files and folder options used by the LiveCode file will point to. The specialFolderPath function lets you quickly point to one of several system folders, such as the documents folder.

I think the documents folder is the best choice for saving project files like these on mobile devices. However, on laptops or desktops it would probably be better to just have the files be saved into the same folder as the application itself. So, I may change the folder path later just for versions of the project meant for laptop/desktop computers.

I also needed a way to guarantee that the file names would be unique, so I created a counter for the number of projects created to date using a file name structure as follows: "VATproject"+{project number}

Contents of a Project File


Here is an example of the contents of a project file titled "VATproject43.txt":

Project Number,43
Project Name,Introduction to z Scores
/Users/lloydrieber/Documents/MOOCs/Presentations/Descriptive Statistics Module/03-z Scores/DescriptiveStatistics-z-Scores.mp4
VATLOG
1,Using Sunny in Example,8070000,8520000,dog,example
2,z Score Formula,32529000,32979000,formulas
3,Girl Scout Example,44334000,44784000,cookies,example
4,Comparing z Scores,78900000,79350000,example
VATCOMMENTS
VATComment,4
An interesting example of how z scores can be used to compare two different distributions.
VATComment,2
The formula is not complicated.
VATComment,3
This is a good context for an example.
VATComment,1
A cute example, especially if you like dogs.
VATTAGS
computation
cookies
dog
example
formulas

Commas are used throughout to separate items on each line. Here are some examples of important, specific information:
  • Item 2 of line 1 contains the project number;
  • Item 2 of line 2 contains the project name given to it by the user;
  • Line 3 contains the path to the video.

Notice the sections that come next, each with a special header: VATLOG, VATCOMMENTS, and VATTAGS. I'm sure you can easily decipher the information that each contains. The number after each comment links the comment to that same numbered video entry. The tags for each video entry are contained at the end of each entry.

Parsing the Project File into the Project Elements


Because there is no limit to how many video entries the user identifies I had to build this text file in such a way as to allow it expand or contract over time. Basically, the script asks LiveCode to scan each line until that header is found, then it knows that all information from the next line down to the next header contains all of the information for that section. For example, here is the code that looks for comments:

   repeat with i = 1 to the number of lines in field "project data"
      if line i of field "project data" = "VATCOMMENTS" then 
         put i+1 into starti
         exit repeat
      end if
   end repeat
   put empty into tempdata
   put 0 into ii
   repeat with i = starti to the number of lines in field "project data"
      add 1 to ii
      if line i of field "project data" = VATTAGS then exit repeat
      put line i of field "project data" into line ii of tempdata
   end repeat
   put tempdata into URL ("file:currentprojectcomments.txt")
   
The first repeat loop looks for the text string "VATCOMMENTS." The second repeat loops takes each line thereafter, up to the point of finding the text string "VATTAGS," and stores it in a temporary local variable called "tempdata." When done, the contents of tempdata are transferred into a text file called "currentprojectcomments.txt." While working on a project, I save the video log, comments, tags, and basic project data (e.g. project name, file path to the movie) in separate text files all with the prefix "currentproject...". Then, I combine these files into one long file and save it with a unique project name. When the user chooses to work on an existing project, this single file is then broken apart and saved into the separate "currentproject" files. I split and recombine this information continuously as the user works on the project.

I debated whether to include some sort of auto-saving, but I wasn't confident I could do this in a way that would be reliable. So, I decided to give the user the option to save manually while working on the project and also save the project automatically when the user chooses to go back to the home card.

I will admit to having some difficulty early on in writing this script. My earlier drafts of this script used the keyword "after" instead of "into" when putting data into a particular line of tempdata. I couldn't get the data stored as expected and if the data gets slightly "out of kilter," nothing will work. So, I gave up on that more elegant and simple (potentially) script in favor of a script that made sure I knew where each line was being saved.

Things to Work on Next


These file management properties of the current version are important improvements, but there are some obvious things that need to be done. First, there are several minor, yet important features that I have not yet tackled:
  • Allow the user to edit the tags assigned to a clip;
  • Allow the user to edit the project name;
  • Error trapping: Catch for the possibility that the user has moved the movie and give the user the opportunity to "find" the movie and re-establish the path.
Also, I need to begin work on the feature of creating reports for each video analysis.

So, I'll focus on these in the next version or two.


Wednesday, May 28, 2014

Lloyd's Video Analysis Tool, Part 5: Major Improvements in Version 0.2.0

I have made good progress on my video analysis tool. So much so that I feel upgrading its status to "version 0.2.0" is warranted. Here are the major tasks I've tackled:
  • Users can now add as many tags as they wish for each video clip;
  • Users can now add a comment that is as long as they wish, extending over as many lines (i.e. paragraphs) as they wish.
  • Users have the option of adding a brief description for each clip.
  • I created a video clip dashboard to display all important important about the selected video clip.
I'll address each of these improvements in this blog post. First, here is a short movie of me demonstrating this prototype:



Please note that I won't be providing any LiveCode files to download as I did in previous posts for this project. For each of those posts, I had created a second and unique version of the project to make clearer the various LiveCode skills and principles I was demonstrating. The project has now progressed to the point where continuing to update and share this second LiveCode file is too time prohibitive. However, I'll continue to share important code where appropriate. And, I think those previous files will help anyone just getting into the use of video within LiveCode.

Attaching Multiple Tags for Each Video Clip


In the previous versions, the user could only add one tag per clip. I knew all along this would be insufficient. Of course, any one video clip could have many tags or no tags, so I needed to account for an uneven number in the design. I debated on the best way to approach this, deciding in the end to just have the tags continue to be added as separate items in the field "log." Doing so created a dilemma about where to put the user comments as they had been also part of this field. I decided to create a separate field for "project comments." This opened up other possibilities for comments, which I'll discuss later in this blog posting.

In previous posts I described the basics of how the tagging system works. Recall that users can create a list of unique tags. Each tag is just a copy and paste of a template button found on the "Library" card where the name and label of the button is changed to whatever tag name the user entered. I revised the script of that template button:

global gActivity
on mouseUp
   //Check to make sure that a clip has been chosen
   if the selectedtext of field "log2" is empty then
      answer "You first have to select an entry from the log."
      exit mouseUp
   end if
   put the label of me into gActivity
   put the hilitedLine of field "log2" into l // read as "line lower case L"; assume this in remaining lines
  
   //count the number of items in the line
   put the number of items of line l of field "log" into varItemsCount
  
   //check to see if tag is already there
   repeat with i = 5 to varItemsCount
      if gActivity = item i of line l of field "log" then
         set the hilitedLine of field "log2" to l
         exit mouseUp
      end if
   end repeat
   put the label of me into item varItemsCount+1 of line l of field "log"
  
   //Update the display of tags
   put "Tags: " into varShowTags
   put the number of items in line l of field "log" into varItemsCount
   if varItemsCount<5 then put "Tags: No tags for this clip" into varShowTags
   //all tags begin with item 5
   repeat with i = 5 to varItemsCount
      put varShowTags&item i of line l of field "log" into varShowTags
      if i<varItemsCount then put varShowTags&", " into varShowTags
   end repeat
   put varShowTags into line 1 of field "Tags"
  
   set the hilitedLine of field "log2" to l
  
end mouseUp

The commented lines show the main parts of this script. It begins by checking to see if the user has chosen a clip and if not, it reminds the user to do so, followed by the command "exit mouseUp." This command tells LiveCode to do just that, exit the script triggered by the mouseUp without executing any other of the lines that follow. I highlighted this line in red. (Notice that I use this command again later in the script.)

The next task is to see if the user has already chosen that tag because we obviously don't want duplicate tags listed for a clip. First,  I check to see how many items are in the highlighted line of field "log" and store this number in the local variable "varItemsCount." That number will be used for the upper limit of a repeat loop coming up, plus we will use it to determine where to put the next unique tag.

Next is the repeat loop, triggered by this line:
repeat with i = 5 to varItemsCount

The local variable i increments by one in each loop, starting with 5 and ending with the value in varItemsCount. I start with 5 because I know the first four item slots are already taken. Item 1 now contains a clip number and item 2 now contains a short description of the clip -- two things I address briefly below. Items 3 and 4 contain the starting and ending time code of the clip in user friendly terms (this was the subject of my most previous post). This repeat loop then checks to see if the tag button just pressed matches any of the tags listed in items 5 and onward. If it finds a match, it exits the mouseUp script. If it doesn't, it adds this tag as the very next item (i.e. varItemsCount + 1).

The next block of script updates the display of the tags in the video clip dashboard. This dashboard is a new feature. Let's address that next.

Video Clip Dashboard


One of the big improvements in version 0.2.0 is the video clip dashboard. Recall that I included some screen placeholders for this video dashboard in the previous versions of the prototype. Well, I finally tackled the implementation of the dashboard here.

I created four fields for the dashboard titled "time code," "Description," "tags," and "display comment." These four fields are populated with the respective information. To populate the tags field, I use another repeat loop to create a string variable called "varShowTags." This contains a list of all the tags with a comma and space in-between, all concatenated as the repeat loops works its magic.

The script ends by highlighting the video clip line in the field "log2."

Updating the Video Clip Dashboard When the User Selects a New Clip


Let's continue to look at the video clip dashboard. When the user clicks on a video entry in the field "log2," the dashboard immediately updates with the information for that clip using the following script attached to the field itself:

on mouseDown
   if the number of lines in me is 0 then exit mouseDown
   stop player "player"
   set the playSelection of player "player" to true
   put the hilitedLine of me into l
   set the currentTime of player "player" to item 3 of line l of field "log"
  
   displayClipInformation l
   displayComment l
end mouseDown

Yes, that's right, you can treat a field like a button and have events triggered if it is clicked. This short script does a lot, thanks to two new commands I created titled "displayClipInformation" and "displayComment." Commands are close relatives to functions. The idea is the same -- you are inventing your own vocabulary and adding them to LiveCode's dictionary. The difference is that a command simply does something without returning any value like a function would (review my description of functions in my previous post). The command displayClipInformation does some magic to populate the fields "time code," "Description," and "tags." The function displayComment is used to populate the field "display comment." Both functions are shown below in their entirety, but I won't try to describe them in any depth here.

Improvement to User Comments


Let's address the design behind the improvement to user comments. I knew I needed to give the user the opportunity to make in-depth comments about any of the clips. This would have to include a basic narrative structure using sentences and paragraphs, along with standard narrative symbols, such as commas and periods. It was clear that the only way to do this was by creating yet another field titled "Project Comments" to store the comments. The tricky part is connecting the right comment to the correct video clip. Since any one comment would potentially contains multiple lines, perhaps dozens, some sort of indexing system would be needed beyond just using the line number of the clip in the field "log2."

So, I used an approach that is adapted from how relational database work. I merely created an indexing system where each clip gets a unique number. I then put the term "VATComment" as item 1 on a line and the index number as item 2 on the very same line. That way, every time the user clicks on a video entry LiveCode searches the "Project Comments" field for that term as item 1 on a line. If it finds it, it then notes the index number. All lines after that and up to the next time the term VATComment is found (or the end of the field is reached), is assumed to be the entire comment for that video clip. Pretty clever if I say so myself.

I also had to design a way for a user to update a particular comment. The best strategy I came up with was to create yet another command to first "seek out and destroy"all lines associated with a particular comment when the user updates a comment, followed immediately by creating a new set of lines for the updated comment. In a sense, every update is really a new comment, as far as LiveCode is concerned. This command is titled "deleteComment" and I've also listed the entire script below. 

Final Thoughts


I also wrote a bunch of other small scripts here and there to have everything run smoothly and to catch user errors. It's really amazing the number of situations one must address when trying to anticipate a user's interaction.

One thing I have not yet done is program a way for the user to delete tags. That will come in the next version of the program. I'm currently mulling over a few ways of doing this, so you'll have to check back later to see how I decide to tackle this issue. Also, I think it is high time I address the need to let the user select any video for analysis. Yes, it is time to put my placeholder video to rest. But, you can always view it here if you miss it.


Appendix: Scripts for the Commands "displayClipInformation" and "displayComment"

Both scripts are defined in the card script. Both scripts take the number corresponding to the selected video clip as input.

command displayClipInformation videoEntryLine
   //Display entry information
   put videoEntryLine into l
   put item 2 of line l of field "log" into field "Description"

   //Construct time code string
   put the timescale of player "Player" into ts
   put item 3 of line l of field "log" into tcStart
   put item 4 of line l of field "log" into tcEnd
   put (tcEnd - tcStart)/ts into varCliplength
   put round(varCliplength,1) into varCliplength
   put convertTimeCode (tcStart) into tcStart2
   put convertTimeCode (tcEnd) into tcEnd2
   put "Clip length: "&tcStart2&" to "&tcEnd2&" ("&varCliplength&" sec)" into line 1 of field "Time Code"
  
   //Determine tags for display
   put "Tags: " into varShowTags
   put the number of items in line l of field "log" into varItemsCount
   if varItemsCount<5 then put "Tags: No tags for this clip" into varShowTags
   //all tags begin with item 5
   repeat with i = 5 to varItemsCount
      put varShowTags&item i of line l of field "log" into varShowTags
      if i<varItemsCount then put varShowTags&", " into varShowTags
   end repeat
   put varShowTags into line 1 of field "Tags"
end displayClipInformation



command displayComment videoEntryLine
   put empty into field "display comment"
   put videoEntryLine into l
   put item 1 of line videoEntryLine of field "log" into videoEntryNumber
  
   repeat with i = 1 to the number of lines in field "project comments"
      if item 1 of line i of field "project comments" is "VATComment" and item 2 of line i of field "project comments" is videoEntryNumber then
         put line i+1 of field "project comments" into line 1 of field "display comment"
         repeat with j = i+2 to the number of lines in field "project comments"
            if item 1 of line j of field "project comments" <> "VATComment" then
               put line j of field "project comments" into line (number of lines in field "display comment"+1) of field "display comment"
            else
               exit displayComment
            end if
         end repeat
         exit displayComment
      end if
   end repeat
   put "No comments yet for this video entry" into line 1 of field "display comment"
end displayComment



command deleteComment videoEntryLine
   //put empty into field "display comment"
   put videoEntryLine into l
   put item 1 of line videoEntryLine of field "log" into videoEntryNumber
  
   repeat with i = 1 to the number of lines in field "project comments"
      if item 1 of line i of field "project comments" is "VATComment" and item 2 of line i of field "project comments" is videoEntryNumber then
         //put line i+1 of field "project comments" into line 1 of field "display comment"
         delete line i of field "project comments"
        
         repeat with j = i to the number of lines in field "project comments"
            if item 1 of line i of field "project comments" <> "VATComment" then
               delete line i of field "project comments"
            else
               exit deleteComment
            end if
         end repeat
         exit deleteComment
      end if
   end repeat
   //put "No comments yet for this video entry" into line 1 of field "display comment"
end deleteComment



Monday, May 19, 2014

Lloyd's Video Analysis Tool, Part 4: Displaying Time Code

As we have seen so far in this series on developing a video analysis tool, LiveCode has its own unique way of tracking intervals per seconds of a video. Each video can have a different number of intervals per second and this can be determined by using the timeScale property. For the video I've been using for testing purposes in my prototype, that number has been 90,000 intervals per second.

So, using this scale, the numbers 37,800,000 to 49,950,000 would mean playing the video from the seven minute mark to the nine minute 15 second mark. The way most of us are used to seeing time code expressed is in this form: hours:minutes:seconds, or...

00:07:00 to 00:09:15

Unfortunately, LiveCode does not have a function to show the time code in this way. But, there is nothing stopping us from inventing our own function to do this. Writing custom functions is a very powerful feature of most programming languages and LiveCode is no exception. Think of it as adding to LiveCode's dictionary. Just as any human language naturally grows with use over time, so too can a computer programming language. So, we'll use this problem as an opportunity to explore how to create functions in LiveCode. I'll name this new function as "convertTimeCode." You can name functions anything you want, assuming you don't pick the name of an already existing function (some programmers add a special prefix or suffix, such as their initials, to each unique function they create to ensure no existing functions names are inadvertently used).

So, what does a function look like in use? Well, we've been using LiveCode built-in functions all along in this blog. The absolute value function is a good example. Consider the following:

   abs(-109)

This returns the number 109. Or, you could say it converts -109 to its absolute value.

Consider these two examples:

abs(598-601)
abs(601-598)

Both return a value of 3. Now consider this block of code:

put 434 into varFirst
put 822 into varSecond
abs(varFirst-varSecond)

This will return a value of 388.

Similarly, let's imagine I've already created the new function "convertTimeCode" and entered the following as part of some scripts:

   convertTimeCode(49950000)
   convertTimeCode(324990000)

These each return values, respectively, of "00:09:15" and "03:00:11".

(Of course, these two examples assume a timeScale value of 90,000; remember, the timeScale value can be different depending on the video. So, we just need to always check for the timeScale when writing our scripts.)

Creating Our Own LiveCode Function


The complete script of the function I created is at the bottom of this post. I'll explain briefly how it works in a moment, but here is its basic construction:

function convertTimeCode ct
     //all of the script that makes the function work goes here
     return myTimeCode
end convertTimeCode

The number that is entered in parentheses when the function is called is put into "ct," a local variable that I get to name. (This was meant to be short for "time code," but I guess I got the letters backwards when I wrote the function!) The script that makes the function work eventually put the final value of the time code into a variable called "myTimeCode." Its value must look like the time code described above (e.g. 01:34:14).

I don't think a full explanation of how this function actually works is needed here. In short, the function takes the number of intervals and converts it to seconds. This is then divided by 3600 -- the number of seconds in an hour -- to figure out how many hours there are. The remainder is then divided by 60 -- the number of seconds in a minute -- to figure out how many remaining minutes there are. The remainder is the number of seconds. I relied on LiveCode's trunc function (short for truncate) for these steps. When you have numbers like 29.35 or 31.78, the trunc function strips away everything to the right of the decimal point, leaving just the integer, such as 29 and 31. No rounding occurs.

Along the way, the script adds extra zeros if and when they are necessary. For example, if there are only 9 minutes for the middle part of the time code, an extra zero is added in front of the 9 (e.g. "09"). Finally, colons are added where needed.

Figuring all this out was just a fun little logic puzzle for me. I'm sure there are probably more elegant ways to do this, but hey, it works.

Displaying the Time Code to the User


Great, we have a function that converts LiveCode's intervals into time code that a person can interpret. Now, what should we do with it? We need to display this time code and have the user interact with it. Of course, this nicely formatted time code isn't what LiveCode uses to actually play video snippets. So, we will continue to need the original video interval information. As in many of my previous LiveCode projects, the strategy here once again points to the use of what I call "Shadow Fields." These are fields that are actually hidden from the user's view, but do work behind the scenes.

I've copied and pasted the field "log" and named this new field simply "log2." I'll put all of the time code data inside log2 in parallel with the interval data in the original log. For now, I'm displaying both so you can see the parallel output, though I'll eventually hide the field "log" (the field "log2" is on the left and the field "log" is on the right):


[ Get the free LiveCode Community version. ]



As you can see, the two lines of the two fields match, or correspond, to each other. All I need to do now is focus the user's interaction on log2 instead of log. But, I have to be careful to use the original log when sending instructions to the movie player. This is easily done because I can refer to the same line number in each field. I just have to be careful when I reference log2 versus log. As an example, here is the revised script for the "Update Start" button:

on mouseUp
   put the hilitedLine of field "log2" into l //note: this is a lower-case L
   put the currentTime of player "player" into ct
   //error trap - starting point must be before ending point
   if ct > item 2 of line l of field "log" then //note: this is a lower-case L
      answer error "Not Allowed! Starting point must come before the ending point!" with "OK"
      exit mouseUp
   end if
   put ct into item 1 of line l of field "log" //note: read this as "into item one of line [lower-case L]"
   put convertTimeCode(ct) into item 1 of line l of field "log2" //ditto
   set the hilitedLine of field "log2" to l //note: this is a lower-case L
end mouseUp

First, notice my use of the convertTimeCode function in the second-to-last line of the script (not counting the "on mouseUp" and "end mouseUp" lines). (Note: Sorry the differences between the number one and the lower-case L are not more readily apparent. I hope my notes in red make this clearer.)

Second, notice how I switch back and forth between referencing log2 versus log. I do the same in the other buttons in the blue editing zone.

One more quick enhancement: Adding an Alert Box


Every time a new entry is added to the log, the previously selected entry is un-highlighted. This causes a problem if the user then immediately clicks on the play button in the editing zone. So, we need to communicate a reminder to the user that they first need to highlight one of the video entries first. We could dim out the play button if nothing is highlighted (which, frankly, sounds like the way to go), but for now I've just added a little alert box in the button "Play entry." Here's the script I added to that button:

   if the selectedtext of field "log2" is empty then
      answer "You first have to select an entry from the log."
      exit mouseUp
   end if

Things are coming along very nicely for this video analysis tool project.


Appendix


Here's the script for my "convertTimeCode" function - impressive, isn't it?

function convertTimeCode ct
   put the timescale of player "Player" into ts
   //Convert intervals into seconds
   put round((ct)/ts) into s
   //Determine number of hours and subtract that number of seconds from total
   if s<3600 then put "00" into myTimeCode
   if s>=3600 then
      put trunc(s/3600) into h
      put s-(h*3600) into s
      if h<10 then
         put "0"&h into myTimeCode
      else
         put h into myTimeCode
      end if
   end if
   //Determine number of minutes
   if s<60 then put myTimeCode&":00" into myTimeCode
   if s>=60 then
      put trunc(s/60) into m
      put s-(m*60) into s
      if m<10 then
         put myTimeCode&":0"&m into myTimeCode
      else
         put myTimeCode&":"&m into myTimeCode
      end if
   end if
   //Determine number of seconds
   if s>=10 then put myTimeCode&":"&s into myTimeCode
   if s<10 then put myTimeCode&":0"&s into myTimeCode
   return myTimeCode
end convertTimeCode