tag:blogger.com,1999:blog-461748190168355992024-03-21T06:18:24.943-07:00Broken BytesRandom thoughts on retroprogrammingtonysavonhttp://www.blogger.com/profile/18275184003580732693noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-46174819016835599.post-82979115395430651232018-03-26T02:11:00.000-07:002018-03-26T02:36:37.219-07:00A 48Khz digital music player for the Commodore 64<i><span style="mso-ansi-language: EN-GB;">Move over, Sennheiser. Make yourself scarce, Bose. Here comes the ultimate Hi-Fi solution for your living room: The Commodore 64!</span></i><br />
<i>I<span style="mso-ansi-language: EN-GB;">s it safe to give up your subscription to Spotify yet? Let's find out.</span></i><br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;"><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves/> <w:TrackFormatting/> <w:PunctuationKerning/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>EN-US</w:LidThemeOther> <w:LidThemeAsian>JA</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:SplitPgBreakAndParaMark/> <w:EnableOpenTypeKerning/> <w:DontFlipMirrorIndents/> <w:OverrideTableStyleHps/> <w:UseFELayout/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="--"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false"
DefSemiHidden="false" DefQFormat="false" DefPriority="99"
LatentStyleCount="371"> <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 2"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 3"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 4"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 5"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 6"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 7"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 8"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 9"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 1"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 2"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 3"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 4"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 5"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 6"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 7"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 8"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Normal Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="footnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="annotation text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="header"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="footer"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index heading"/> <w:LsdException Locked="false" Priority="35" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="caption"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="table of figures"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="envelope address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="envelope return"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="footnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="annotation reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="line number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="page number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="endnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="endnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="table of authorities"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="macro"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="toa heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 5"/> <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Closing"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Signature"/> <w:LsdException Locked="false" Priority="1" SemiHidden="true"
UnhideWhenUsed="true" Name="Default Paragraph Font"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Message Header"/> <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Salutation"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Date"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text First Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text First Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Note Heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text Indent 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Block Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Hyperlink"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="FollowedHyperlink"/> <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/> <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Document Map"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Plain Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="E-mail Signature"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Top of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Bottom of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Normal (Web)"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Acronym"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Cite"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Code"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Definition"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Keyboard"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Preformatted"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Sample"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Typewriter"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Variable"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Normal Table"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="annotation subject"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="No List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Outline List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Outline List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Outline List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Simple 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Simple 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Simple 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Colorful 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Colorful 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Colorful 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table 3D effects 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table 3D effects 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table 3D effects 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Contemporary"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Elegant"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Professional"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Subtle 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Subtle 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Web 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Web 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Web 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Balloon Text"/> <w:LsdException Locked="false" Priority="39" Name="Table Grid"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Theme"/> <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/> <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading"/> <w:LsdException Locked="false" Priority="61" Name="Light List"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/> <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/> <w:LsdException Locked="false" Priority="34" QFormat="true"
Name="List Paragraph"/> <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/> <w:LsdException Locked="false" Priority="30" QFormat="true"
Name="Intense Quote"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/> <w:LsdException Locked="false" Priority="19" QFormat="true"
Name="Subtle Emphasis"/> <w:LsdException Locked="false" Priority="21" QFormat="true"
Name="Intense Emphasis"/> <w:LsdException Locked="false" Priority="31" QFormat="true"
Name="Subtle Reference"/> <w:LsdException Locked="false" Priority="32" QFormat="true"
Name="Intense Reference"/> <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/> <w:LsdException Locked="false" Priority="37" SemiHidden="true"
UnhideWhenUsed="true" Name="Bibliography"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/> <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/> <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/> <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/> <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/> <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/> <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 6"/> </w:LatentStyles> </xml><![endif]--><!--[if gte mso 10]> <style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Table Normal";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin-top:0cm;
mso-para-margin-right:0cm;
mso-para-margin-bottom:8.0pt;
mso-para-margin-left:0cm;
line-height:107%;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:"SwissReSans",sans-serif;
mso-ansi-language:EN-US;}
</style> <![endif]--> </span></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/UYAf_awh5XA/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/UYAf_awh5XA?feature=player_embedded" width="320"></iframe></div>
<br />
<h4>
<span style="mso-ansi-language: EN-GB;"><div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Preface</span></div>
</span></h4>
<div class="MsoNormal">
</div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;"> </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Unlike my previous posts, this article requires a good understanding of 6502 Assembly. If you are still wasting your time with the likes of Java, Python, or whatever they told you it is in fashion in 2018, you might want to reconsider your priorities: it's not too late to learn this magnificent programming language. You might even <a href="https://www.geek.com/news/nasa-seeks-programmer-fluent-in-60-year-old-languages-to-work-on-voyager-1638276/">land a job at NASA</a>. You will thank me later. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">As readers of these pages know, I've always been obsessed with audio and video compression for humble machines. My game <a href="http://rgcd.bigcartel.com/product/planet-golf-commodore-64">Planet Golf</a> for the Commodore 64 even includes <a href="https://www.youtube.com/watch?v=qIRww2a59lE">Full Motion Video</a> running from a floppy disk. The problem with this stuff, though, is that, as much as it's interesting to see these experiments run on such a limited piece of HW, and as much as it feels like an achievement to the programmer, that doesn't change their gimmicky nature. In other words, let's be honest, no person in their right frame of mind would waste a second of their time listening to those scratchy sounds, unless deafened by unconditional love for the machine. Spoiled as we are with high quality sound coming from all kinds of devices around us, poor Commodore 64 cannot be our to-go solution for our aural pleasure. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Or can it?</span></div>
<div class="MsoNormal">
<br /></div>
<h4 class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Mission</span></h4>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">To build a C64 software player that can play a whole song at 48Khz (higher frequency than CDs' 44.1Khz) using a stock Commodore 64 and a regular ROM cartridge, which is your typical 80s setup.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Now, there are all kinds of devilish pieces of hardware available for your Commodore 64 nowadays, such as 16Mb RAM Expansion Units, or even mp3 hardware players. Of course, this stuff was not around in the 80s, and it therefore does not appeal to purists. In other words, any reliance on these monstrosities would get me disqualified. You might as well run a marathon riding a motorbike. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The largest "legitimate" ROM Cartridges are those that Ocean used for their games. You can store a whopping one megabyte of data onto them. We are going to need all of it!</span></div>
<div class="MsoNormal">
<br /></div>
<h4 class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">In Numbers</span></h4>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">So, we are playing 8-bit samples at 48Khz from one megabyte of storage, minus few Kbytes for the actual player code. This leaves us with 48Kb to encode one second of uncompressed audio, and means that the whole cartridge can barely contain 20 seconds of audio. Which means that we must implement some form of compression, ideally at least 4:1 to get close to 1m30 sec of audio. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Commodore 64's CPU runs at roughly 1Mhz. This means that, if we want to play 48000 samples per second, we only have about 100000/48000 = 21 CPU clock cycles per sample.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Now, how does playing a sample occur? C64 can play 8-bit samples, using a trick that has been recently invented by <a href="https://livet.se/mahoney/">Pex Tufvesson</a>. The oscillators must be configured and locked in a certain way, and after that, writing to the volume register generates a shift in the voltage, which in turn makes the sample audible. Unfortunately, unlike the 4-bit playing technique, this shift is not linear with the sample value. Therefore, one needs to read an 8-bit entry from memory, use that entry to fetch an amplitude value from a LUT, and finally write this amplitude value into the SID volume register, $d418. Assuming that our first sample sits at $8400, and that our 256 entries lookup table sits at $1000, this is what we need to do to play the first sample.</span><br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> ldx $8400
lda $1000,x
sta $d418
</code></pre>
<br />
<span style="mso-ansi-language: EN-GB;"><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves/> <w:TrackFormatting/> <w:PunctuationKerning/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>EN-US</w:LidThemeOther> <w:LidThemeAsian>JA</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:SplitPgBreakAndParaMark/> <w:EnableOpenTypeKerning/> <w:DontFlipMirrorIndents/> <w:OverrideTableStyleHps/> <w:UseFELayout/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="--"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false"
DefSemiHidden="false" DefQFormat="false" DefPriority="99"
LatentStyleCount="371"> <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 2"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 3"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 4"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 5"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 6"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 7"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 8"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="heading 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index 9"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 1"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 2"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 3"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 4"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 5"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 6"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 7"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 8"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" Name="toc 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Normal Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="footnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="annotation text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="header"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="footer"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="index heading"/> <w:LsdException Locked="false" Priority="35" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="caption"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="table of figures"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="envelope address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="envelope return"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="footnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="annotation reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="line number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="page number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="endnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="endnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="table of authorities"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="macro"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="toa heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Bullet 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Number 5"/> <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Closing"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Signature"/> <w:LsdException Locked="false" Priority="1" SemiHidden="true"
UnhideWhenUsed="true" Name="Default Paragraph Font"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="List Continue 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Message Header"/> <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Salutation"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Date"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text First Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text First Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Note Heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Body Text Indent 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Block Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Hyperlink"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="FollowedHyperlink"/> <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/> <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Document Map"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Plain Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="E-mail Signature"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Top of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Bottom of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Normal (Web)"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Acronym"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Cite"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Code"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Definition"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Keyboard"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Preformatted"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Sample"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Typewriter"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="HTML Variable"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Normal Table"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="annotation subject"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="No List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Outline List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Outline List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Outline List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Simple 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Simple 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Simple 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Classic 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Colorful 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Colorful 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Colorful 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Columns 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Grid 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table List 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table 3D effects 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table 3D effects 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table 3D effects 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Contemporary"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Elegant"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Professional"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Subtle 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Subtle 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Web 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Web 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Web 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Balloon Text"/> <w:LsdException Locked="false" Priority="39" Name="Table Grid"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
Name="Table Theme"/> <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/> <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading"/> <w:LsdException Locked="false" Priority="61" Name="Light List"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/> <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/> <w:LsdException Locked="false" Priority="34" QFormat="true"
Name="List Paragraph"/> <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/> <w:LsdException Locked="false" Priority="30" QFormat="true"
Name="Intense Quote"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/> <w:LsdException Locked="false" Priority="19" QFormat="true"
Name="Subtle Emphasis"/> <w:LsdException Locked="false" Priority="21" QFormat="true"
Name="Intense Emphasis"/> <w:LsdException Locked="false" Priority="31" QFormat="true"
Name="Subtle Reference"/> <w:LsdException Locked="false" Priority="32" QFormat="true"
Name="Intense Reference"/> <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/> <w:LsdException Locked="false" Priority="37" SemiHidden="true"
UnhideWhenUsed="true" Name="Bibliography"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true"
UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/> <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/> <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/> <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/> <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/> <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/> <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46"
Name="Grid Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51"
Name="Grid Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52"
Name="Grid Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46"
Name="List Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51"
Name="List Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52"
Name="List Table 7 Colorful Accent 6"/> </w:LatentStyles> </xml><![endif]--><!--[if gte mso 10]> <style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Table Normal";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin-top:0cm;
mso-para-margin-right:0cm;
mso-para-margin-bottom:8.0pt;
mso-para-margin-left:0cm;
line-height:107%;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:"SwissReSans",sans-serif;
mso-ansi-language:EN-US;}
</style> <![endif]--> </span><br />
<code></code><span style="mso-ansi-language: EN-GB;">These three instructions take 4 cycles each, so 12 clock cycles out of the 21 we can afford are gone just to play one sample, and we haven't even added the loop logic and the compression!</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">This means that whatever form of compression we come up with, it must be extremely efficient and snappy. Possibly, again, a simple look up from a table.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;"><a href="https://en.wikipedia.org/wiki/Vector_quantization">Vector Quantization</a> springs to mind: we need a set of 4-byte codebooks so that, splitting the original waveform in blocks of 4 adjacent samples, each of these blocks can be represented by an index in the codebook table, and ideally the number of codebooks should be 256, so that we can use a single byte as an index. This yields the 4:1 compression ratio that we are seeking, but to use only 256 codebooks for a whole song and maintain a good quality is wishful thinking. That is why most vector quantization based compression codecs need a dynamic codebook set. As the playing progresses, least recently used codebooks are discarded and replaced with fresher ones encoded in the actual bitstream. With so few clock cycles available, though, we can't even think of implementing such a complex scheme, so our approach will be to periodically replace all of the codebooks.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Although not optimal from the point of view of the quality, this solution actually plays very well along the limitations of the cartridge, which is operated by means of bank switching. One could think of switching bank and switching codebooks at the same time.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Each bank consists of 16Kbytes. If we use 256 4-byte codebooks for each of these banks, we "waste" 1Kb for the codebooks set and are left with 15 Kb to store 15*1024 entries which will translate to 15*1024*4 samples. The added footprint of the codebooks reduces the compression ratio from 4:1 to 3.75:1, but we can live with that. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">We'll see later what the encoder looks like, although there'll be no huge surprises.</span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h4>
<span style="mso-ansi-language: EN-GB;">The play routine</span></h4>
</div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The cartridge layout consists of 64 banks of 16 Kbytes each. Within the commodore 64 address space, each of these banks can be mapped to $8000-$BFFF. By writing values from $00 to $3f to cart register $de00 we can switch-in one of the 64 banks. We'll place our set of codebooks for each bank at the beginning of the dataset, in an interlaced way. So the first byte of the first codebook will be at $8000, the second will be at $8100 and so on. The n-th byte of the m-th codebook will be at $8000 + n*$100 + m</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">So our player will look like:</span></div>
<div class="MsoNormal">
<br /></div>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> For (bank = $00; bank <= $3F; bank++)
{
bank->$de00 //select bank
For (i = $8400; I < $c000; i++)
{
Id = mem[i]
Sample0 = mem[$8000 + Id]
SYNC()
PLAY(Sample0) //store value to $d418
Sample1 = mem[$8100 + Id]
SYNC()
PLAY(Sample1) //store value to $d418
Sample2 = mem[$8200 + Id]
SYNC()
PLAY(Sample2) //store value to $d418
Sample3 = mem[$8300 + Id]
SYNC()
PLAY(Sample3) //store value to $d418
}
}
</code></pre>
<br />
<br />
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The SYNC() pseudo function is the key here. The actual playing, which means writing the sample value to register $d418, must occur exactly every 21 clock cycles. In fact, it's not that important that the actual frequency of 48000Hz is precisely matched (few Hz more or less won't make that much of a difference), what really counts is that the same frequency is maintained. What would happen if we played one of those 4 samples in the loop, say, 20 cycles after the previous one? That would add an ear-piercing high-frequency phase noise distortion to the sample. This kind of jittering is not a problem at very low frequencies (we'll use that to our advantage), but at 12Khz (1 in every 4 samples) it will seriously affect the quality of the output.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Now that we know all the constraints, let's take a look at a possible first version of the player. Which plays an entire bank from the cartridge.</span></div>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">
sample: ldx $8400
ldy $8000,x
lda sidtable,y
sta $d418
nop //wastes 2 cycles
nop //wastes 2 cycles
nop //wastes 2 cycles
bit $ea //wastes 3 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>ldy $8100,x
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>lda sidtable,y
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>sta $d418
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>nop //wastes 2 cycles
</code><code style="color: black; word-wrap: normal;"> </code><code style="color: black; word-wrap: normal;">nop //wastes 2 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>nop //wastes 2 cycles
</code><code style="color: black; word-wrap: normal;"> </code><code style="color: black; word-wrap: normal;">bit $ea //wastes 3 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>ldy $8200,x
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>lda sidtable,y
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>sta $d418
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>nop //wastes 2 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>nop //wastes 2 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>nop //wastes 2 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>bit $ea //wastes 3 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>ldy $8300,x //4 cycles
</code><code style="color: black; word-wrap: normal;"> </code><code style="color: black; word-wrap: normal;">sidtable,y //4 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>sta $d418 //4 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> </code>inc sample + 1 //6 cycles
bne sample //2 cycles if jump does not occur,
//3 otherwise
inc sample + 2
lda sample + 2
cmp #$c0
bne sample
</code></pre>
<br />
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;"><span style="mso-tab-count: 4;"> </span></span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Now - this does what we need in terms of decoding and playing the data, but timing is completely screwed up. If you check the "distance" between two consecutive operations STA $d418, for the first three samples it takes exactly 21 cycles, but problems arise when you get to the last block. Altering the sample read address and looping back takes a variable amount of cycles. In case we are not crossing the page boundary, when the bne sample instruction actually jumps, we are very lucky, because the loop takes exactly 21 cycles until the next STA $d418. On the other hand, if the jump does not occur, the number of cycles until the next STA $d418 becomes 35!</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">One could say that this only happens once every 256 code entries, so once every 1024 samples, and that is not a big deal, after all. Unfortunately, at these high frequencies this means that the distortion would still be quite noticeable. We must get rid of those final 14 cycles. Since every 16Kb cart block only has 15Kb of actual code data, which is 60 pages, one could think of unrolling the external loop. Let's give it a try:</span></div>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> For (i=0; i<60; i++)
{
sample: ldx $8400 + i*$100
ldy $8000,x
lda sidtable,y
sta $d418
nop //wastes 2 cycles
nop //wastes 2 cycles
nop //wastes 2 cycles
bit $ea //wastes 3 cycles
ldy $8100,x
lda sidtable,y
sta $d418
nop //wastes 2 cycles
nop //wastes 2 cycles
nop //wastes 2 cycles
bit $ea //wastes 3 cycles
ldy $8200,x
lda sidtable,y
sta $d418
nop //wastes 2 cycles
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> nop //wastes 2 cycles</code>
</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> nop //wastes 2 cycles</code>
bit $ea //wastes 3 cycles
ldy $8300,x //4 cycles
lda sidtable,y //4 cycles
sta $d418 //4 cycles
inc sample + 1 //6 cycles
bne sample //2 cycles if jump does not occur,
//3 otherwise
}
</code></pre>
<br />
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">That is starting to look better: each sample plays exactly after 21 cycles, like in the previous version. There are still a couple of issues, though. When the last sample is played, the <span style="font-family: "courier new" , "courier" , monospace;">bne sample</span> instruction only takes 2 cycles, so the following sample is played after 20 cycles, not 21. One cycle less every 1024 samples is not as bad as 14 more, but still not acceptable. More than this, though, there's a serious problem with loop unrolling on the C64: When branches occur across pages, they take one cycle more! Some of these 60 play routines will certainly end up crossing page boundaries, hence screwing up the timing once again. What we can do is to align the loops at the page boundary, but this would add gaps in between the play routines. These gaps must be jumped over with a <span style="font-family: "courier new" , "courier" , monospace;">jmp</span> instruction, which in turn adds another 3 cycles, once again screwing up the timing. In fact, if you take a look at what happens to the end of the first play routine, you'll see something like this:</span></div>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> sta $d418 //4 cycles
inc sample + 1 //6 cycles
bne sample //2 cycles if jump does not occur,
//3 otherwise
jmp nextblock //3 cycles
.align </code></pre>
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">sample: ldx $8400 + i*$100 //4 cycles
ldy $8000,x //4 cycles
lda sidtable,y //4 cycles
sta $d418 //4 cycles
</code></pre>
<br />
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">27 cycles!</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">It's a bit like solving a Sudoku puzzle, isn't it? And this is getting a bit boring too, so, without further ado, I present you with the final version:</span></div>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> .for (var i=0; i<60; i++)
{
l: nop
sample: ldx $8400 + i*$100
ldy $8000,x
lda sidtable,y
sta </code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">$d418</code>
inc sample +1
bit $ea
ldy $8100,x
lda sidtable,y
sta </code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">$d418</code>
inc $d020
bit $ea
ldy $8200,x
lda sidtable,y
sta </code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">$d418</code>
bit $ea //3
nop //5
ldy $8300,x //9
lda sidtable,y //13
ldy sample + 1 //17
sta </code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">$d418</code> //21
bne l //2 cycles. 3 cycles if jumps
//to the first nop
jmp nextblock+1 //3 </code></pre>
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">.align 64 //the whole code is less than 64 bytes
//so aligning to 64 should fit 4 blocks in one page,
//while still preventing cross-page branching</code></pre>
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;"> nextblock:
//there'll be a nop here,
// but it will not be executed at the first run
}
</code></code></pre>
<br />
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">If you check all the possible "paths" within this code, you'll find out that writes to $d418 always occur 21 clock cycles apart, no matter the flow. The secret was to "spread" all of the loop handling logic across the 4 sample play sections in each loop, leveraging those sync dummy operations to do something useful rather than active wait, thus saving precious cycles in the last block to accommodate the <span style="font-family: "courier new" , "courier" , monospace;">jmp</span> to the next page play routine. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Now each of the 1024*60 samples played from a cartridge bank plays exactly 21 cycles after the previous one, and we even managed to squeeze in an <span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "courier new" , "courier" , monospace;">inc</span> $d020</span>, to show some raster effect on the screen.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Now I hear you say, there must still be some code to switch banks, and that will waste precious clock cycles, adding some delay.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">You are right, but we don't have to do anything about that. We are talking about an off-sync every 60000 sample, that is 1.28 seconds: a phase noise at a frequency that is lower than 1hz, which is not audible. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Job done.</span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h4>
<span style="mso-ansi-language: EN-GB;">Songs</span></h4>
</div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">1:30 might sound like a tight limitation, but there are still plenty of songs that come in at less than 90 seconds. I ended up choosing Elvis Costello's "Welcome to the Working Week", from his debut album <i style="mso-bidi-font-style: normal;">My Aim is True</i>, and my favourite piece of music from my favourite progressive band: Jethro Tull's "Cheap Day Return", from the seminal record <i style="mso-bidi-font-style: normal;">Aqualung</i></span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Both <i style="mso-bidi-font-style: normal;">Aqualung</i> and <i style="mso-bidi-font-style: normal;">My Aim is True</i> are available in digitally remastered edition in 96khz 24bit lossless format:<span style="mso-spacerun: yes;"> </span>plenty of quality to start from.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/cKwLkNoySI4/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/cKwLkNoySI4?feature=player_embedded" width="320"></iframe></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/UYAf_awh5XA/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/UYAf_awh5XA?feature=player_embedded" width="320"></iframe></div>
<br /></div>
<div class="MsoNormal">
<h4>
<span style="mso-ansi-language: EN-GB;">The encoder</span></h4>
</div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Vector Quantization is an easy approach to achieve decent compression with minimal impact on the decoder performance. In fact, in the setup I've used, it only takes a single lookup from the codebook table to fetch a sample. In order to obtain about X:1 compression, one needs to split a given PCM vector of n samples into n/x vectors of x consecutive samples, with x=4 in our case. This would be the input to a round of clustering, typically <a href="https://en.wikipedia.org/wiki/K-means_clustering">K-means</a>, with K=256 in our case being the number of codebooks we want to compute.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Since K is a power of 2, we can also use <a href="https://en.wikipedia.org/wiki/Linde%E2%80%93Buzo%E2%80%93Gray_algorithm">LBG</a>, which, in this case, performs slightly better. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">But more than the clustering algorithm, what really makes the difference here is the way we can prepare the data. Rather than using the original PCM waveform, I worked with a delta representation of the square root of each sample. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Let's say that <i>A </i>is our waveform with <i>-1.0<<span style="font-family: "swissresans" , sans-serif; font-size: 11.0pt; line-height: 107%;">a<sub>i</sub></span><1.0</i> for each sample, our clustering pass runs instead on the pseudo waveform <i>B </i>which is defined as </span><span style="mso-ansi-language: EN-GB;"><span style="mso-ansi-language: EN-GB;"><span style="font-family: "swissresans" , sans-serif; font-size: 11.0pt; line-height: 107%;"> </span></span></span><br />
<br />
<i><span style="mso-ansi-language: EN-GB;"><span style="mso-ansi-language: EN-GB;"><span style="font-family: "swissresans" , sans-serif; font-size: 11.0pt; line-height: 107%;">b<sub>i</sub></span></span> = sign(</span><span style="mso-ansi-language: EN-GB;"><span style="mso-ansi-language: EN-GB;"><span style="font-family: "swissresans" , sans-serif; font-size: 11.0pt; line-height: 107%;">a<sub>i</sub></span></span>)*sqrt(|</span><span style="mso-ansi-language: EN-GB;"><span style="mso-ansi-language: EN-GB;"><span style="font-family: "swissresans" , sans-serif; font-size: 11.0pt; line-height: 107%;">a<sub>i</sub></span></span>|) – sign(</span><span style="mso-ansi-language: EN-GB;"><span style="mso-ansi-language: EN-GB;"><span style="font-family: "swissresans" , sans-serif; font-size: 11.0pt; line-height: 107%;">a<sub>i-1</sub></span></span>)*sqrt(|</span><span style="mso-ansi-language: EN-GB;"><span style="mso-ansi-language: EN-GB;"><span style="font-family: "swissresans" , sans-serif; font-size: 11.0pt; line-height: 107%;">a<sub>i-1</sub></span></span>|)</span></i></div>
<div class="MsoNormal">
<br />
<span style="mso-ansi-language: EN-GB;">The square root works as a poor man's amplification filter for high-frequency parts or for fricative sounds that typically come with low amplitude. The clustering pass would otherwise zero these finer details. The use of differential pcm, instead, brings in the idea that transitions, more than absolute values, are what we actually hear in a waveform.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Of course the set of codebooks that we obtain from this clustering pass is not representative of the original waveform <i>A</i>, only the clusters are. Therefore, we must recompute the centroids of the clusters using the actual vectors from <i>A</i>.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">Higher values of K can be used, which lead to higher compression rates at the cost of quality. </span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">K=4, as used in the demo, brings in a minimal degradation. I've experimented with higher values and K=8 is still kind of acceptable, and allows for almost 3 minutes of music to fit on a cartridge, but of course much depends on the type of content that we are encoding.</span><br />
<span style="mso-ansi-language: EN-GB;">In the following picture you can see, in order, 500msec of the original, uncompressed audio, the differential on the squared signal used as input for the encoder, and the codebook-reconstructed 8-bit waveform, which is what the C64 plays</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-BMnTiNi8tOo/WrgPw-ZPanI/AAAAAAAAG2I/iy_Nt2ROlcQGzcTS1EMGyHSzH4_bdS-JwCLcBGAs/s1600/waveforms.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="662" data-original-width="1600" height="165" src="https://4.bp.blogspot.com/-BMnTiNi8tOo/WrgPw-ZPanI/AAAAAAAAG2I/iy_Nt2ROlcQGzcTS1EMGyHSzH4_bdS-JwCLcBGAs/s400/waveforms.png" width="400" /></a></div>
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h4>
<span style="mso-ansi-language: EN-GB;">Further Development</span></h4>
</div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">I got a lot of "Why don't you use a 16Mb REU?" As I said, I'm not really keen on using HW that is not true to the spirit of the 80s. REUs (Ram Expansion Units) did exist in the 80s, but only came in 128, 256 and 512kb cuts. 16Mb REUs are just a modern thing and, although one could in theory put an entire record onto a 16Mb REU with this encoder, where would the fun be? One advantage that REUs have over a cartridge, though, is that, being RAM, you can actually write it. This means that the amplitude values for a specific SID chip could be hardcoded to the codebooks before we play the song, and the lookup from the sidtable would no longer be needed at playtime, freeing 4 precious clock cycles per sample, and thus paving the way to even higher sampling frequencies, if that even makes sense. How about a 64Khz compressed-audio player? Not sure the difference would be audible and that it would make much sense, but again nothing here makes sense in first place, so why not?</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The second most asked question is, "Will you release the encoder"? Well, the encoder is just a bunch of Python scripts at the moment, and it's described in this very blog post, so in theory anyone can recreate it now, but I guess it would still be nice to release a user-friendly application for those who don't want to mess around with scikit-learn. If I am not distracted by even more ridiculous projects, that is what I am doing next. </span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h4>
<span style="mso-ansi-language: EN-GB;">Reception</span></h4>
</div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The demo was well-received and got quite some attention. I shared the YouTube previews right before I uploaded the <a href="http://csdb.dk/release/?id=162941&show=review">actual demo to CSDB</a>, just because that requires a bit more time. Still, those few minutes that passed between the two actions caused a lot of people to shout "fake!", despite the fact that I had stated that the download would have been available shortly. I guess that "it sounds so good I think it's fake" counts as the most flattering of the compliments!</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The demo is currently number 9 in CSDB's <a href="http://csdb.dk/toplist.php?type=release&subtype=%281%29">top C64 demos of all time</a>, which, you could say, is a bit of an overestimation of the actual value of the production. And I totally agree.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The fact is that the demo doesn't really break any new ground: Vector Quantization has been used in few productions already (although I'm not sure about the combination with DPCM), and high-frequency digi playing has been a thing for quite some time, mostly thanks to the oustanding work of <a href="https://livet.se/mahoney/">Pex Tufvesson</a>. I guess that doing the two things together, on a proper song, rather than a second or two of speech, was a low hanging fruit that someone had to reap at some point. I was happy to do it, but the real innovators are people like Pex.</span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h4>
<span style="mso-ansi-language: EN-GB;">Appendix</span></h4>
</div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">I also developed a 3-byte codebook version of the player, so that I could use a less destructive compression for songs that do not make it past the 60-second mark. This was a bit trickier because there are fewer cycles available in the main loop to "spread" the instructions controlling the loop itself. At some point, I needed to add 1 cycle to the last playing write, which is not all that easy because no instruction takes less than 2 cycles, so you can only add 2. I ended up slowing down the read from sidtable just for that single write, which can be done using the y indirect addressing mode.</span></div>
<div class="MsoNormal">
<span style="mso-ansi-language: EN-GB;">The "Mazinger Z" song, encoded with this technique, really sounds as good as the original.</span></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/Q2Me-tpEI6w/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/Q2Me-tpEI6w?feature=player_embedded" width="320"></iframe></div>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">.for (var i=0; i<61; i++)
{
l: nop
sample: ldx $8300 + i*$100
ldy $8000,x
lda sidtable,y
sta $d418
inc sample +1
bit $ea
ldy $8100,x
lda sidtable,y
sta </code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">$d418</code>
sty $d020
ldy $8200,x
lda ($fb),y //like lda sidtable,y but a cycle more
ldy sample + 1
sta </code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">$d418</code>
bne l
jmp nextblock+1
.align 64
nextblock:
}
</code></pre>
<br />
<br />tonysavonhttp://www.blogger.com/profile/18275184003580732693noreply@blogger.com174tag:blogger.com,1999:blog-46174819016835599.post-37931117370357501132017-07-30T11:51:00.000-07:002017-07-31T01:41:35.841-07:00One small step for man, one giant leap for the Commodore 64<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif;">
</div>
<h2>
<span style="font-size: 12.8px;">The making of Planet Golf - Part 1</span></h2>
<span style="font-size: 12.8px;">As of July 28th, Planet Golf, my latest work for Commodore 64, is available. If you have not done so already, go check it out! And maybe <a href="https://goo.gl/B1m5U6">buy a copy too</a>: All the proceeds go to Unicef. It can't be that bad, after all.</span><br />
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/5ote5Jqpkgs/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/5ote5Jqpkgs?feature=player_embedded" width="320"></iframe></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
It has been a fantastic one-year ride and to see it finally come to completion, with a nice old style packaging, feels like an enormous achievement. Benefiting from the help of incredibly talented composers and artists, I could finally focus solely on the actual game design and programming. In fact, Planet Golf has so many new aces up its sleeve compared to <a href="http://brokenbytes.blogspot.co.uk/2015/03/the-making-of-p0-snake-part-1.html">P0 Snake</a>, the first game that I published, that I thought it would have been interesting to blog about all the things I learned in the process. I am starting with what I believe is the most intriguing one: Full Motion Video.</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<h4 style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
Why FMV?</h4>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
The B-side of the floppy (yes, back then floppy disks had two sides. You could manually flip the disk and enjoy an additional 170Kb of 8-bit greatness) comes with a bunch of extras, one of them being "Planet Golf – the story so far". The game takes place in the year 38911, when humanity has colonized most of the planets of the solar system, and space tourism has been a reality for many centuries. So much so that the first interplanetary golf tournament is about to start. But how did we get there?</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
In the 80s, most games that would have benefited from a preface got away without this, providing the user with a leaflet or a section in the manual.</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-2eAXmzMG54o/WX3tk9cAmBI/AAAAAAAAGjc/4gomyKEBYk0dhNhuOlAxGJ0KtepHDicDgCLcBGAs/s1600/IMG_20170730_151224.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="836" data-original-width="1600" height="206" src="https://4.bp.blogspot.com/-2eAXmzMG54o/WX3tk9cAmBI/AAAAAAAAGjc/4gomyKEBYk0dhNhuOlAxGJ0KtepHDicDgCLcBGAs/s400/IMG_20170730_151224.jpg" width="400" /></a></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px; text-align: center;">
<i>Enlightenment User manual</i></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
There were few noticeable exceptions though, especially in the late 80s, when disk drives had become more affordable and software houses had started investing in this media, thus relinquishing the constraints of tapes (painfully slow and sequential-only loads). One of my favourite was Last Ninja 3, by system 3. This game has a cinematic intro that fills in the player with what happened since the end of the previous chapter of the trilogy.</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-N9e5hDZUZbg/WX3whbQPyCI/AAAAAAAAGjo/gLA34dIXj4Mej6p3A6VmRihb2CvSbq2WACLcBGAs/s1600/ninja.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="400" data-original-width="640" height="250" src="https://1.bp.blogspot.com/-N9e5hDZUZbg/WX3whbQPyCI/AAAAAAAAGjo/gLA34dIXj4Mej6p3A6VmRihb2CvSbq2WACLcBGAs/s400/ninja.png" width="400" /></a></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px; text-align: center;">
<i style="font-size: 12.8px;">Frames from Last Ninja III Cinematic Intro</i></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<i><br /></i></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
It came very late in the life of the Commodore 64, at a time when players had already been spoiled by the marvels of console games and 16-bit computers, but few of us were still playing with the humble Commodore 64 mainly for nostalgia. Yet, the first time I saw it, I remember being totally blown away by the technical achievement and the impeccable implementation. Not only that: most importantly, I thought that this intro was not just a blatant show-off of the programming prowess of the developers, but it rather added depth to the game. You could see that there had been a development in the story, there was a path that was being followed. Of course, to watch it now, in the times of Hollywood game productions, will put a smile on your face. But you have to bear in mind that when this came out, players on the Commodore 64 were used to a static screen with credits and the evergreen "press fire button to play". Cause that is what games were for, and still are. But Last Ninja 3 was one of the first pioneering outings in the industry that tried to close the gap between videogames and other type of media. And if you are still not impressed with this, think that Robin Levy lit each of those pixels, for each of those frames, one by one. And that was a time when mice were not very common yet, so maybe he did so with a joystick!</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
With this intro in mind I thought it would have been nice to explain what had happened in the universe of Planet Golf before 38911 with a "cinematic" preface to the game. In doing so, I tried to somehow up the ante and add full motion video coming from real footage. Pretty much for one simple reason: I'm no Robin Levy and I couldn't really see myself drawing all those pixels by hand!</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
First, I thought I would put an excerpt from Kennedy's famous speech to the congress in which, following the first successes of the Mercury space program, he basically announced funding for NASA's Gemini and Apollo missions that would eventually put a man on the moon.</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<span style="font-size: 12.8px;">This is also known as the "</span><a href="https://www.nasa.gov/vision/space/features/jfk_speech_text.html" style="font-size: 12.8px;">Kennedy Challenge</a><span style="font-size: 12.8px;">" and it is an incredibly inspiring speech, belonging to a time when politics encouraged (and empowered) people to come together to accept and overcome challenges, rather than putting them against each other over problems. But this is another story!</span></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<span style="font-size: 12.8px;"><br /></span></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/YCcz7SypgHw/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/YCcz7SypgHw?feature=player_embedded" width="320"></iframe></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="background-color: white; color: #222222; font-family: "arial" , sans-serif; font-size: 12.8px;"><i>The Kennedy Challenge</i></span></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="background-color: white; color: #222222; font-family: "arial" , sans-serif; font-size: 12.8px;"><i><br /></i></span></div>
<div class="separator" style="clear: both; text-align: center;">
<i style="color: #222222; font-family: arial, sans-serif; font-size: 12.8px; text-align: start;">"I believe that this nation should commit itself to achieving the goal, before this decade is out, of landing a man on the moon and returning him safely to the earth"</i></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
I thought I would also alternate credits with more footage from the Apollo missions, like the Saturn V lift-off and a moonwalk. Oh, and of course the famous countdown and the iconic "Euston, the Eagle has landed!" sequence had to be there too. I also envisioned a speaker doing some voiceover, plus some solemn music playing in the background. </div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<i><br /></i></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-L6DOlAXEBiY/WXzDIIpaL6I/AAAAAAAAGjQ/kImfJz2O3EQXtkgWV-41KBXYCPJUHEDuwCLcBGAs/s1600/storyboard.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1200" height="640" src="https://2.bp.blogspot.com/-L6DOlAXEBiY/WXzDIIpaL6I/AAAAAAAAGjQ/kImfJz2O3EQXtkgWV-41KBXYCPJUHEDuwCLcBGAs/s640/storyboard.jpg" width="480" /></a></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px; text-align: center;">
<i>Bask in the glow of my storyboard!</i></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
<br /></div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
With the script out of the way I was left with the following figures: In total we are talking about roughly 70 seconds of digitized speech and music, and a grand total of 30 seconds of video alternated with few stills for the credits, making up for the remaining 40 seconds.</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
After we have taken off the data for the other extras, There are about 120 Kbytes of floppy disk space left for the cinematic intro and we need to squeeze-in all the aforementioned components, plus of course the code to use them. There’s no doubt we are going to use the whole 120 Kbytes for this task, but this also means that, besides audio and video, there’s a third component that we have to take care of, and it’s I/O. The commodore 64 gets his name from the amount of memory it comes with, that is 64Kbytes. Therefore we need to “stream” the data from the floppy disk while the video and the audio keep playing. Quite ambitious, isn't it? And, let me remind you, the Commodore 64 is equipped with a 1Mhz CPU capable of 8-bit integer math, no less. There is no doubt we will have to come down to compromises.</div>
<div class="MsoNormal" style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px;">
I’ll see you in the next post, where we’ll dive into the technical details of the implementation and see how, once again, the limitations of this machine can become its strength.</div>
tonysavonhttp://www.blogger.com/profile/18275184003580732693noreply@blogger.com3tag:blogger.com,1999:blog-46174819016835599.post-51480599526695245002015-06-17T07:49:00.000-07:002015-06-17T07:53:15.919-07:00P0 Snake 64Kb, grab your copy today!Just a very brief post to say that P0 Snake is finally out for sale, so grab your copy NOW:<br />
<br />
<a href="http://rgcd.bigcartel.com/product/p0-snake-commodore-64">http://rgcd.bigcartel.com/product/p0-snake-commodore-64</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-OYEJIW7RZUI/VYGHd-Oqm9I/AAAAAAAAFpY/s3VHqfBNhss/s1600/delux_pack02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://3.bp.blogspot.com/-OYEJIW7RZUI/VYGHd-Oqm9I/AAAAAAAAFpY/s3VHqfBNhss/s400/delux_pack02.png" width="375" /></a></div>
<br />
<div>
<br /></div>
<div>
<br /></div>
<div>
This is the <b>real deal,</b> guys: You get a nice big box with stickers, a poster, a booklet, a password table and, of course, your P0 Snake cartridge. Just hook your Commodore 64 to your telly, plug the cartridge in and play this game the way it's meant to be played! </div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Enjoy</div>
<div>
<br /></div>
<center>
<video controls="" height="272" poster="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmdWxIU1p1MXNHMHc" width="384">
<source src="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmYTU4dW8teHhzRGc" type="video/mp4"></source>
Your browser does not support the video tag.
</video>
</center>
tonysavonhttp://www.blogger.com/profile/18275184003580732693noreply@blogger.com3tag:blogger.com,1999:blog-46174819016835599.post-4599135807692337732015-04-10T02:06:00.000-07:002015-04-10T02:35:36.101-07:00The Making of P0 Snake - Part 3: Audio Compression For Poor CPUs<h3>
Introduction</h3>
<div>
<div>
Compression of digital sound has been a subject of study for some 50 years now, during which researchers have produced a wide number of audio codecs, that is systems to compress (or encode) sound and to decompress (or decode) it to something that can be played. We mentioned mp3 already, which is based on psychoacoustic modelling and was designed for music, and GSM, which is a very simple algorithm to encode speech, but there are many more. What pretty much all of these systems have in common is that the decoder does not return the original waveform, but an approximation of it. They are therefore always referred to as lossy audio codecs. We have seen already that even the introduction of heavy modifications in the original digitized waveform, like changing the frequency to 8000hz or moving from 16 to 4 bit quantization, does indeed affect the quality of the sound, but, especially for speech, it retains its "understandability". There are algorithms like CELP that can encode speech to as low as 800 bytes per second. Unfortunately, we can only dream of using this kind of technology in this context, for two simple reasons: first, any audio decoder would need space just for the code. What good would it be to compress our 9 seconds of audio to 4KB if we need, say, a 20KB decoder to play it? Second, and most importantly, audio decoders are too heavy on the CPU. In fact, for a computer like the Commodore 64, whose CPU only runs at 1mhz and is only capable of 8 bit integer math, even just playing the uncompressed samples is a demanding task!</div>
<div>
How demanding? Let’s find out. (Warning: what follows might be a bit boring and very C64-centric, so you might want to take my word for it that playing digitized audio is very demanding and just skip to the next section.)</div>
<div>
<br /></div>
We mentioned already that the Commodore 64 was not designed to play digitized sound. The way programmers managed to do so was by exploiting a bug in the sound chip, the SID. Basically, this chip allows the programmer to set the volume to one of 16 different levels via a 4 bit register. Incidentally, the action of setting the volume creates a side effect, an audible “click”. The amplitude of this click is proportional to the volume; therefore changing the volume repeatedly at a steady frequency approximates the playing of any waveform. That’s very fortunate! So, to play the 7884Hz 4 bit waveform we have talked about so far, one should just alter the volume register 7884 times per second and feed it with the right sample value. Now, to keep the frequency so steady is the real problem here. Luckily, the C64 comes with a programmable interrupt system that can be driven by a timer. Without going too much into detail, what the Commodore 64 can do for you is to execute a piece of code every time the timer counts down to zero and restart it automatically for you while invoking your piece of code, which we’ll call <i>interrupt handler</i>. Anything that the CPU was doing when your interrupt handler is invoked is in fact interrupted and your interrupt handler routine takes control. This is beautiful, as in theory we can just program this routine to read the next sampled value from memory, put it in the volume register and exit, thus returning control to whatever the CPU was doing when it was interrupted. And that’s exactly what P0 Snake or any other talking game does.<br />
<div>
The problem is that <i>whatever the CPU was doing</i>, it was doing so by using its registers, and our sound play routine will use the same registers to do its thing. This means that, upon returning control, our routine must make sure that the registers contain exactly the same values that they contained when the CPU was interrupted. This is achieved by saving all the registers somewhere as the first thing in the interrupt routine and then restoring all of them as the last thing before returning control. This somewhere is usually the system stack. Unfortunately, only one of the registers, the Accumulator, A, can write to the stack. The other registers, X and Y can’t. But they can write to the Accumulator! </div>
<div>
We now have the default skeleton code for a timer driven interrupt service routine, which will look like this:</div>
</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> pha //push the Accumulator onto system stack. [3]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> txa //transfer X to Accumulator [2]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> pha //push accumulator again, thus saving X value. [3]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> tya //transfer Y to Accumulator [2]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> pha //push the Accumulator once again, thus saving Y value [3]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> //our sample playing routine here, </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> //which can now use the 3 registers</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span><br />
<span style="font-family: Courier New, Courier, monospace;"> [...]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> pla //pops Accumulator from the stack [4]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> tay //transfer Accumulator to Y [2]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> pla //pops Accumulator [4]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> tax //transfer Accumulator to X [2]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> pla //pops the Accumulator [4]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> rti // return to whatever CPU was doing,</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> //All the registers correctly restored [6]</span></div>
<div>
<br /></div>
</div>
<div>
<br /></div>
<div>
The numbers in the square brackets are the number of cycles that the instruction takes. Remember that the Commodore 64 is clocked at 1Mhz, or 1000000 cycles per second, so let’s see what the overhead of all this saving and restoring registers takes us to:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">3+2+3+2+3+4+2+4+2+4+6 = 35</span></div>
<div>
<br /></div>
<div>
<div>
So for each call to the interrupt routine, 35 cycles go to waste without doing anything. Zero, zip, nada! We said that the highest sampling frequency at which we end up calling this routine is 7884 times per second, which means every second 7884 * 35 cycles are wasted. That’s 275940 cycles per second, which is roughly 28% of the CPU horsepower.</div>
<div>
<br /></div>
<div>
To cut a long story short, 28% of the CPU goes to waste just because of the overhead of this saving and restoring the registers, and we haven’t really played anything yet! Therefore, we can't really think of adding anything very complex on top of that if we want to be able to ALSO run the game at the same time. </div>
<div>
<br /></div>
<div>
Although there are tricks involving self modifying code (yuk!) that will allow you to take that number down, and P0 Snake uses all of them, this number still gives us an idea of how demanding this task is for the CPU, therefore on the fly audio decompression is out of the question here.</div>
</div>
<div>
<br /></div>
<h3>
Compression</h3>
<div>
<div>
Let’s summarize our findings so far:</div>
<div>
<br />
<ul>
<li>We squeezed our original source audio into 21KB.</li>
<li>We want to land somewhere in the neighborhood of 4KB.</li>
<li>We accept to have a short wait before the game runs to have the 4KB of compressed audio decompressed to the 21KB that we are going to use from that moment on.</li>
<li>The decoder should be a fairly short program, whose memory footprint should be negligible compared to the 4KB of the compressed audio.</li>
<li>It should be really fast! No fancy floating point, no weird math, nothing like that. Possibly LUTs or Dictionaries but little more than that.</li>
</ul>
</div>
<div>
<br /></div>
<div>
Having ruled out lossy audio compression schemas, classic, lossless compression approaches spring to mind. <a href="http://en.wikipedia.org/wiki/LZ77_and_LZ78">LZ77</a> is at the base of most of these approaches, such as RAR, ZIP, 7Z, you name them. It’s based on the idea that multiple occurrences of the same subsequence in a sequence can be encoded by using a single copy of such subsequence and references to the same data for the other copies of the subsequence. </div>
<div>
A string like </div>
<div>
<br /></div>
<div>
<div style="text-align: center;">
<span style="font-family: Courier New, Courier, monospace;">ABCDABCDABCDE </span></div>
</div>
<div>
<br /></div>
<div>
is encoded to something like</div>
<div>
<br /></div>
<div>
<div style="text-align: center;">
<span style="font-family: Courier New, Courier, monospace;">ABCD(-4,4)(-8,4)E</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
The pairs (i,j) between parentheses mean “go back i symbols and copy j symbols from there”.</div>
<div>
<br /></div>
<div>
While finding such repeated copies is a computationally intensive task, such a task is up to the encoder. The decoder, which is what we are most interested in, has a fairly straightforward job to do. Straightforward enough to be implemented on the C64 without too much pain. We are almost there! </div>
<div>
The only problem left is that LZ77, being a lossless compression approach, is not expected to compress much. We are lucky if we’ll see a 30-40% reduction (you can do this experiment yourself: try to record a wav file and zip it). That would take our memory footprint down to 12-15 KB. Much better, but not quite there yet.</div>
<div>
<br /></div>
<div>
But what if we manage to make LZ77 encoder’s life easier? What if we could somehow maximize the number and the size of identical subsequences of samples?</div>
<div>
<br /></div>
<div>
Let’s bring up once again that piece of the “Oh No” waveform we have mentioned in part 2. </div>
<div>
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-MOhTTbaVLwU/VSbvVL6q2PI/AAAAAAAAFlE/eh6g4fVI39I/s1600/oh_20msec_5000_.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-MOhTTbaVLwU/VSbvVL6q2PI/AAAAAAAAFlE/eh6g4fVI39I/s1600/oh_20msec_5000_.png" /></a></div>
<br /></div>
<div>
<br /></div>
<div>
We said that it almost looks like it was made up of the same segment repeated over and over. Let’s bring up these segments.</div>
<div>
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: left;">
<a href="http://2.bp.blogspot.com/-dWJP16nhukA/VSbvbjWLyqI/AAAAAAAAFlM/8Cq6MCIQhHg/s1600/oh_20msec_5000_blocks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-dWJP16nhukA/VSbvbjWLyqI/AAAAAAAAFlM/8Cq6MCIQhHg/s1600/oh_20msec_5000_blocks.png" /></a></div>
<br /></div>
<div>
<br /></div>
<div>
That’s very close to what LZ77 needs to do its dirty job at its best, but not quite that, yet. There are, in fact, minimal differences among the blocks. If this waveform was really made exactly of the same segment repeated over and over, LZ77 would only need one of those segments plus a few indices to encode the entire waveform in the picture. </div>
<div>
Which brings us to the following intuition: how bad would it be (sound) to just use one of these blocks and repeat it over and over to form the actual waveform? In this case, the differences are so minimal that we probably wouldn't be able to appreciate them, and LZ77 would encode this piece to roughly one fifth of its original size! So, all we need to do is try to find all the similar blocks, make them identical, and run a normal LZ77 compression pass to have something that will unpack easily and will sound reasonably similar to the unaltered waveform. We don't really need fancy approximate pattern matching algorithms here, given how small the amount of data we are talking about is, and we could just go the brute force route. The algorithm would be something like this:</div>
</div>
<div>
<br /></div>
<div>
<div style="margin-left: 36.0pt;">
<span style="font-family: Courier New, Courier, monospace;">encode(waveform, threshold)</span><br />
<span style="font-family: Courier New, Courier, monospace;">{</span><br />
<span style="font-family: Courier New, Courier, monospace;"> i=0;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> while (i < length(waveform))</span><br />
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: Courier New, Courier, monospace;">{</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for
(blocksize = 64; blocksize > 4; blocksize--)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: Courier New, Courier, monospace;">j
= <i>find_most_similar_block in range (0,i) </i></span></div>
<div style="margin-left: 36.0pt;">
<span style="font-family: Courier New, Courier, monospace;"><i> of size blocksize</i>;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: Courier New, Courier, monospace;"> if
(similarity((j,j+blocksize),</span></div>
<div style="margin-left: 36.0pt;">
<span style="font-family: Courier New, Courier, monospace;"> (i,i+blocksize)) < threshold(blocksize))</span><br />
<span style="font-family: Courier New, Courier, monospace;"> { </span></div>
<div style="margin-left: 36.0pt;">
<span style="font-family: Courier New, Courier, monospace;"> // replace the current block with a copy </span></div>
<div style="margin-left: 36.0pt;">
<span style="font-family: Courier New, Courier, monospace;"> // of a similar one</span><br />
<span style="font-family: Courier New, Courier, monospace;"> copy
((j,j+blocksize), (i,i+blocksize))</span></div>
<div style="margin-left: 36.0pt;">
<span style="font-family: Courier New, Courier, monospace;"> i+=blocksize;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> break;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"> else //no
similar block of any size was found</span><br />
<span style="font-family: Courier New, Courier, monospace;"> {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> i++; // keep the current sample value and go
ahead.</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"><o:p> </o:p></span><span style="font-family: Courier New, Courier, monospace;"> return LZ77encode(waveform);</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<div class="Normal1">
<o:p></o:p></div>
</div>
<div>
<br /></div>
<div>
<div>
Dealing with blocks smaller than 4 samples doesn’t really make much sense, because the overhead for the indexes that LZ77 needs to replicate the blocks would outsize the length of the block itself. </div>
<div>
The most important part of this algorithm is the similarity function, which needs to be able to compare two blocks of a given size and return a distance between them. The temptation of going straight with the <a href="http://en.wikipedia.org/wiki/Euclidean_distance">Euclidean distance</a> is strong. And in fact we’ll give in to that, but there’s one last thing to speech encoding that must considered. The dynamic range of speech is not linear (which is a fact that some basic audio compression schemas, like <a href="http://en.wikipedia.org/wiki/A-law_algorithm">a-law</a>, leverage). Basically, for our concerns this means that an alteration to samples that are “close” to zero will introduce a heavier distortion than the same absolute alteration to samples that are at the boundary of the sample range. To account for this, assuming such a range to be between [-1,1], rather than using the absolute sample value, we’ll go with the logarithm of the amplitude.</div>
<div>
Ultimately, given two vectors of samples <i>P</i> and <i>Q</i>, whose values range in [-1,1], the distance between the two vectors is</div>
</div>
<div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-BgfZshe0pP8/VSbvBhqJwMI/AAAAAAAAFk8/_fAvsDUcGWk/s1600/equation.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-BgfZshe0pP8/VSbvBhqJwMI/AAAAAAAAFk8/_fAvsDUcGWk/s1600/equation.png" height="50" width="400" /></a></div>
<br />
<br /></div>
<div>
<span style="font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Arial;">It’s important to note that this logarithmic decay is taken into account only when computing the distance between two blocks, but the waveforms we end up storing are the original, linear sampled versions.</span></span></div>
<div>
<span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<h2>
<span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">Final tricks and conclusion</span></h2>
<div>
<div>
<span style="vertical-align: baseline;"><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">Although there are many more tricks/hand tuning that I had to resort to in order to improve compression and/or reduce the compression artifacts, the encoding algorithm we discussed so far does the largest part of the job, and puts us in a position in which the commodore 64 only has to LZ77-decode 4KB of data to 21KB before the game starts. Now the only thing we need to feed the encoder with is the right threshold, which is dynamically adjusted by the encoder according to the size of the vectors it compares (this part would probably deserve a post on its own).</span></span></span></div>
<span style="vertical-align: baseline;">
</span>
<br />
<div>
<span style="vertical-align: baseline;"><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span></span></div>
<span style="vertical-align: baseline;">
</span>
<br />
<div>
<span style="vertical-align: baseline;"><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">We can basically try different thresholds until we are satisfied with the total size of the encoded waveform, which is 4KB in our case, and go for it. Of course a higher threshold will cause more blocks to be marked as similar and lead to a better compression, at the cost of a decrease of sound quality.</span></span></span></div>
<span style="vertical-align: baseline;">
</span>
<br />
<div>
<span style="vertical-align: baseline;"><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span></span></div>
<span style="vertical-align: baseline;">
</span>
<br />
<div>
<span style="vertical-align: baseline;"><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">In fact, I used different thresholds for different sentences in the game, which leads me to the very last trick that I want to tell you about. Listen again to the speech:</span></span></span></div>
<span style="vertical-align: baseline;">
</span>
<div>
<span style="vertical-align: baseline;"><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span></span></div>
<span style="vertical-align: baseline;">
<div>
<span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">
<video controls="" height="272" poster="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmdWxIU1p1MXNHMHc" width="384">
<source src="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmbzBiZ2k4R0pRVVU" type="video/mp4"></source>
Your browser does not support the video tag.
</video>
</span></span></div>
<div>
<span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span></div>
<div>
<span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">You might have noticed that the worst sounding sentence of the bunch is the first one, “Welcome to P0 Snake”. It will surprise you to know that this part is actually the one that has been compressed the least and takes up half of the total space of 4KB alone. It should therefore sound better than the rest. Believe it or not, it actually does. I can further explain with an example: in the silence of the night you can probably hear both your watch ticking and your neighbour talking in another flat. Now, suppose you take your neighbour out for dinner in a crowded restaurant. You can still hear what she is saying, but even if you focus on your watch, you won't be able to hear it ticking. </span></span></div>
<div>
<span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">This phenomenon is called </span><a href="http://en.wikipedia.org/wiki/Auditory_masking" style="font-size: 15px; white-space: pre-wrap;">Auditory Masking</a><span style="font-size: 15px; white-space: pre-wrap;">, and it’s just one of the many concepts you'll get familiar with, should you decide to dive into the fascinating world of </span><a href="http://en.wikipedia.org/wiki/Psychoacoustics" style="font-size: 15px; white-space: pre-wrap;">Psychoacoustics</a><span style="font-size: 15px; white-space: pre-wrap;">. Glitches and artifacts introduced by compression are like the ticking of your watch as your neighbour talks during the night. You can hear them in a quiet environment, but if you bring in more sound to “confuse” the ear, they'll become unnoticeable. That’s what I did with the remaining set of sentences in the game: whenever the game is talking, there’s always music playing along with the speech. It’s a bit like hiding the dust under the carpet, but, well… it works!</span></span></div>
<div>
<span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span></div>
<div>
<span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">This concludes the third and last part about speech encoding. Next time we'll probably be looking at something a bit more geeky, like self modifying code or graphics compression.</span></span></div>
<div>
<span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;">Stay tuned!</span></span></div>
<div style="font-family: Arial; font-size: 15px; white-space: pre-wrap;">
<div style="text-align: center;">
<br /></div>
</div>
</span></div>
tonysavonhttp://www.blogger.com/profile/18275184003580732693noreply@blogger.com1tag:blogger.com,1999:blog-46174819016835599.post-83992591363851567282015-03-16T10:49:00.001-07:002015-04-10T02:36:02.521-07:00The Making of P0 Snake - Part 2: IT TALKS!<h2 style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;">
<span style="font-family: 'Trebuchet MS'; font-size: 17px; line-height: 1.38; white-space: pre-wrap;">Introduction</span></h2>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">One of the features that stood out about P0 Snake, according to the people who played it, is its digitized speech. </span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<video controls="" height="272" poster="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmdWxIU1p1MXNHMHc" width="384">
<source src="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmbzBiZ2k4R0pRVVU" type="video/mp4"></source>
Your browser does not support the video tag.
</video>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Now, mind you, talking games have been around for quite some time, and they were not exactly uncommon in the 80s either. The problem with speech though, is that it takes “a lot” of memory, Therefore, while modern productions can get very chatty (even too much), usually old games could only afford few seconds of speech, before they filled up the host machine’s memory. But it's probably this scarcity that contributed to making the few words that came out of the talking video games so memorable. Elvin Atombender, the mad scientist in </span><a href="http://en.wikipedia.org/wiki/Impossible_Mission" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Impossible Mission</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, would welcome you to his lab with his signature “</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Another visitor… stay a while… stay forever!</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">” that would send shivers down your spine. Also, with speech being such an "expensive" commodity, programmers had to resort to it only where it really added something to the gameplay. </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="http://en.wikipedia.org/wiki/Mega_Apocalypse" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Mega Apocalypse</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, another all-time favourite of mine, was so hectic that sometimes players didn't know what they were doing, such was the focus on trying to stay alive. So the game just told them what was going on. “</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Extra life!</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">”, and the player knew he had hit something good, not a cursed asteroid.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">With P0 Snake things were a bit tricky though. The RGCD 16Kb development competition, as the name suggests, only allows 16Kb games. So, how much speech can we fit into 16Kb? Or, even better, how much speech can we squeeze into 16Kb and still have enough space left to code a game? Let’s find out!</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 17px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Enter “The Dictionary”</span></h2>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: right;">
<span style="background-color: white; color: #373737; font-family: Arial; font-size: 15px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“When words are scarce they are seldom spent in vain.”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: right;">
<span style="background-color: white; color: #373737; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[William Shakespeare]</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">P0 Snake's dictionary is made up of 7 sentences:</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Welcome to P0 Snake”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Get Ready”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“One Up!”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Teleport!”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Oh no!”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Well Done!”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">‘Game Over”.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In total, they account for roughly </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">9 seconds</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> of speech.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It doesn't sound like a big deal, but, trust me, it is. Let’s see why.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A sound is basically a waveform, or, for our purposes, the digital representation of its analog form. This process of analog-to-digital conversion has two steps: sampling and quantization. A signal is sampled by measuring its amplitude at a particular time; the sampling rate is the number of samples taken per second. In order to accurately measure a wave, it is necessary to have at least two samples in each cycle: one measuring the positive part of the wave and one measuring the negative part.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Of course having more than 2 samples per cycle will increase the accuracy and the quality of the waveform representation, but what’s really important is that you don’t have LESS than 2 samples per cycle, as this would cause the frequency of the wave to be completely missed. If you want to know more about this stuff, just look up the </span><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"><a href="http://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem">Nyquist-Shannon Sampling Theory</a></span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> and start from there, I’m trying to keep this simple. What all of this is telling us anyway, is that in order to digitize sound properly, we must keep a sample rate that is at least twice the frequency of the signal we are sampling. The 44.1 Khz frequency at which your CD track or your mp3s are sampled can render a signal of up to 20 Khz, which is enough to capture all the frequencies that the human ear can hear. If mp3 was made for dogs, it would need to use a sample rate higher than 120Khz, as dogs can hear frequencies of up to 60Khz. If it was made for cats, it would need to be more than 160Khz! It’s good that we evolved from apes and not from cats, otherwise our ipod could only contain a fourth of the music it contains now!</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">And there are more good news: human voice only spans a narrower set of frequencies which goes up to approximately 3500Hz. It means that if you want to sample speech you just need a sample rate of at least 7000hz to keep it intelligible. Indeed, most speech encoding systems, including GSM whose acronym probably rings a bell (ahem…) for most of you, only use 8000Hz. That’s the kind of frequency we are really dealing with, and this closes the math for the sampling, the first part of the analog to digital conversion. What about quantization? In general, the more the better, and your typical CD track, to go back to the original example, uses 16 bit, 2 bytes, which means that each sample is a number in the range (-32768, 32767). Let’s assume we are using the same resolution. We have all the numbers now, so let’s see how much space those 9 seconds of speech will eat up.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">9 seconds X 8000 samples per second X 2 bytes per sample = 144000 bytes = </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">140 Kb</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Given that we have to fit both speech AND a game into 16 Kb that’s a bit too much. We should target 4 or 5 Kb at most. It’s a long way uphill from here...</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The first little help comes, again, from a limitation of the machine. 16 bit samples simply can’t be played on the Commodore 64. In fact, The SID, the Commodore 64 sound chip, was not designed to play sampled sound at all. The way programmers did it was by exploiting a bug in the sound chip. I won’t go into details as to how it works (try </span><a href="http://sid.kubarth.com/articles/the_c64_digi.txt" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">this</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> if you dare), but, simply said, although a better quantization can be achieved with weird tricks, the most common way of playing sampled sound on a Commodore 64 only allows for </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">4 bit</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> resolution. This means that the range we can address is not (-32768,32767) but (-8,7). Since 4 bits are one fourth of 16 bit, our original space consumption goes down to 35Kb from 140kb. That’s a lot better, but still more than twice the space we have for the entire game.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Before we move on, let’s take a look at what these waveforms we have been talking about look like. For example the piece of speech “Oh No!”.</span></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
</div>
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-Vr77ENikyic/VQQcB3Do7zI/AAAAAAAAFbo/jNEK_6jUVgk/s1600/ohno_wav_.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-Vr77ENikyic/VQQcB3Do7zI/AAAAAAAAFbo/jNEK_6jUVgk/s1600/ohno_wav_.png" /></a></div>
<br />
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">From top to bottom, the original recording, the same one sampled at 8000Hz with 16bit quantization, and finally the same one at 8000Hz with 4 bit quantization.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">As you can see, the fact that only 16 different amplitude values are possible with 4 bit quantization shows in the form of the waveform being a bit "blocky" at a glance. This really represents the BEST waveform we can think of playing on the Commodore 64, and, despite the appearance, it doesn’t sound that bad. This is what we really have to start from anyway.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Before we even start to think about compression, let’s see if we can bring memory occupation down from the current 35Kb</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s zoom in a bit, and let’s explore “the “oh” part of “oh no!”, going back to the original 16 bit quantization and 44.1Khz sampling,that is CD-quality. This is what 20 milliseconds of “oh” look like</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-N5SN49QGfl4/VQQdiKSvEwI/AAAAAAAAFcQ/F0VpJWs2sTw/s1600/oh_20msec_44100_.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-N5SN49QGfl4/VQQdiKSvEwI/AAAAAAAAFcQ/F0VpJWs2sTw/s1600/oh_20msec_44100_.png" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We immediately notice something: the waveform looks very “regular”, as if it was made of the same piece repeated many times, with just minimal differences. We'll take advantage of this aspect when we deal with the compression, but for now another interesting piece of evidence is that there aren't really many “parts” in this waveform, that is it doesn't change very rapidly. We mentioned that the frequency of human voice tops at 3500hz, and that we need double that frequency, that is 7000Hz which we had rounded to 8000Hz, to sample it. But I bet you we need much less than that for this specific segment.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s see what it looks like at 8000hz:</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-4ijP6QcleH0/VQQduzJKKxI/AAAAAAAAFcY/ddMH_YdYxc0/s1600/oh_20msec_8000_.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-4ijP6QcleH0/VQQduzJKKxI/AAAAAAAAFcY/ddMH_YdYxc0/s1600/oh_20msec_8000_.png" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It looks very similar, but we knew this already, as we know that 8Khz will suffice for human voice in general</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">let’s see what happens at 5000hz</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-kYX7jg3IJeM/VQQeA0Zk1dI/AAAAAAAAFcg/OUIldrEPPog/s1600/oh_20msec_5000_.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-kYX7jg3IJeM/VQQeA0Zk1dI/AAAAAAAAFcg/OUIldrEPPog/s1600/oh_20msec_5000_.png" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It’s starting to deteriorate a bit, still, all the transitions are there and this means that the “oh”, although “noisy”, will still sound like an “oh”. Let’s try 1000Hz</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-QwaR2vVHx1s/VQQehto81VI/AAAAAAAAFcs/5Tl34cjLanE/s1600/oh_20msec_1000_.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-QwaR2vVHx1s/VQQehto81VI/AAAAAAAAFcs/5Tl34cjLanE/s1600/oh_20msec_1000_.png" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Bummer! Most of the transitions have disappeared. This waveform doesn't sound like an “oh” anymore. But what we take from this exercise is that we don't always need to use 8000Hz. Some pieces of our speech are happy with a lower sample rate. </span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Speech fragments can be divided into two categories: Voiced and Unvoiced. Voiced sounds are generated by the vocal cords’ vibration. Among their characteristics is the fact that they are periodic, and their period is called </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">pitch</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">. It’s the case for vowels and specifically the fragment that we have analyzed so far. Unvoiced sounds, on the other hand, are not generated by the vibration of the vocal cords and they don’t have a specific pitch. Furthermore, and most importantly, they come with a higher frequency. It’s the case for </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: italic; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">fricative </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sounds like “F” or “S”.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; margin-left: 1em; margin-right: 1em; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img height="65" src="https://lh6.googleusercontent.com/tknQxu0apIpdajy5R3rUWBX71qPCrHLfDH_2U3HNgTa8W8ExZSGxi5vEU9wetUrl2eLWOax1MBD-yU6q6Fjuh8kKIdib2dTIsmQbbwYBXieOGqwI40QlpyWzuwK55ZUXuXW2J4c" style="-webkit-transform: rotate(0.00rad); border: none; transform: rotate(0.00rad);" width="400" /></span></div>
</div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Now this is interesting, and you can see already where this is going: let’s use a higher sample rate for unvoiced segments and a lower sample rate for voiced segments.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We can validate this theory looking at another segment of P0 Snake’s speech. The “ZE” part in “welcome to P </span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ZE</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ro Snake”.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-V1FMyoWGqqY/VQQfGCjFzmI/AAAAAAAAFc0/I7Mr8p0DLto/s1600/ze_150msec_44100_.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-V1FMyoWGqqY/VQQfGCjFzmI/AAAAAAAAFc0/I7Mr8p0DLto/s1600/ze_150msec_44100_.png" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">You can clearly see where the “Z” ends and where the “E” starts already. Now let’s sample this segment at 5000Hz, which worked quite well in the previous example</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-j3JlQ4Vm8y4/VQQffn23pQI/AAAAAAAAFc8/OnIae-ybgDo/s1600/ze_150msec_5000_.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-j3JlQ4Vm8y4/VQQffn23pQI/AAAAAAAAFc8/OnIae-ybgDo/s1600/ze_150msec_5000_.png" /></a></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<br />
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Look what just happened: the “Z” has almost completely gone but the “E” is still there. </span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In conclusion, we really need all of the 8000Hz to sample fricative sounds and preserve their understandability.</span><br />
<span style="font-family: 'Trebuchet MS'; font-size: 17px; font-weight: bold; line-height: 1.38; white-space: pre-wrap;"><br /></span>
<span style="font-family: 'Trebuchet MS'; font-size: 17px; font-weight: bold; line-height: 1.38; white-space: pre-wrap;">Putting everything together</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">So far we have learned the following facts:</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">4 bit amplitude is all we can afford</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">8000Hz is a sampling rate that allows any type of speech to be coded.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Fricative sounds require this entire bandwidth.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Voiced sounds will be happy with much less. How much less? It depends on the sound, the voice of the speaker and other factors.</span></div>
</li>
</ol>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">So, in order to minimize the memory occupation of the speech in P0 Snake, we just need to use the minimum sampling frequency for each speech segment that retains the understandability of the segment. This frequency will be higher for unvoiced segments and lower for voiced segments.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Although we could choose ANY frequency for each of the segments we really want to limit this search to a small set of possible frequencies for various reasons, the most important of which will be clear in the next part, but we can say already that we also want to be able to encode this frequency somehow in the bitstream. P0 Snake uses only 4 frequencies, so it requires a 2 bit overhead to indicate the frequency for each speech segment:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: center;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">S = {3500Hz, 3942Hz, 5256Hz, 7884Hz}</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">And the algorithm to preprocess the data is now quite easy:</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">S = [3500Hz, 3942Hz, 5256Hz, 7884Hz]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">segments = split(source) </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">//splits the source in segments </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 144pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">//of equal duration</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">foreach (segment in segments)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">f = Frequency_Analysis(segment)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">i = 0</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">while (i < 3 && S[i] < f) </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">i = i + 1</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sampled_segment = Sample(segment,S[i])</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">yield return( (i,sampled_segment) )</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The result of this preprocessor was only good as a starting point for a (painful) manual refinement of each segment: although the frequency analysis lib I used worked very well for me, I found that for some segments I could still move to the lower frequency, keeping a decent sound quality. This pickiness might sound like overkill, but don't forget that we are fighting with bytes here, so every little helps!</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In the end, all the speech in P0 Snake averages at roughly 4900hz. Let’s see where this takes us up to:</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">35Kb * 4900/8000 = ~21Kb</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">21 Kb</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">. Now... we are talking! It’s still 4 times larger than what we are targeting, and (as we'll see in the next post), we can't use any known audio codec to bring this number down, because the Commodore 64 simply doesn't have enough horsepower to play a sampled sound AS it decompresses it AND to also runs a game. Therefore, we must play uncompressed data. But this number already looks very interesting for one simple reason: The rules of the competition are that the size of the game must not exceed 16 Kb, but of course we can use the entire memory space of the Commodore 64 at runtime, that is 64Kb. If we could come up with a way to compress our speech in 4Kb in a way that can be decompressed (in a reasonable time) to 21 Kb BEFORE the game runs, then that would be it.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We'll see how in the next post, if you wish.</span></div>
<br />tonysavonhttp://www.blogger.com/profile/18275184003580732693noreply@blogger.com1tag:blogger.com,1999:blog-46174819016835599.post-30369073590692129752015-03-11T09:10:00.000-07:002015-03-13T02:59:50.195-07:00The Making of P0 Snake - Part 1<h1 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;">
<span style="background-color: transparent; color: black; font-family: 'Trebuchet MS'; font-size: 21px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Preface</span></h1>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625"></span><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625"><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">More than 20 years ago my teenage self had a dream. To be more exact, my teenage self shared the same boring dream with most of my friends. That dream was to create a video game for the Commodore 64. It sounds silly today, but that’s what every teenager wanted to do back in the 80s. You wanted to be an astronaut, a football player or a game developer. Not surprisingly, I did not make a career in space travel or any kind of professional sports and, although I went on to publish several games for modern platforms many years later, I didn’t make it my main occupation. Which is not something I regret now. What I really regretted was failing to write my own Commodore 64 game. I’ll spare you the details of what made me try to rectify that situation, although I guess it could simply be summarised by a friend of mine telling me that it is never too late (yes, it’s that simple), but anyway, fast forward a couple of decades and </span><span style="font-family: Arial; font-size: 15px; font-style: italic; vertical-align: baseline; white-space: pre-wrap;">P0 Snake</span><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">, my entry in the</span><a href="http://www.rgcd.co.uk/2014/12/rgcd-c64-cartridge-development.html" style="text-decoration: none;"><span style="color: black; font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #1155cc; font-family: Arial; font-size: 15px; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">RGCD 16Kb game development competition</span></a><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">, came <a href="http://csdb.dk/event/?id=2230">first</a> and will be published later this year. Better late than never, you silly daydreaming teenager!</span></span></div>
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625">
</span>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div class="separator" style="clear: both; text-align: center;">
</div>
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625"><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"> </span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625"><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"><a href="http://4.bp.blogspot.com/-t6MvRJTp6EI/VQBzpY_5kcI/AAAAAAAAFaM/LoE4mmocF6k/s1600/snap.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-t6MvRJTp6EI/VQBzpY_5kcI/AAAAAAAAFaM/LoE4mmocF6k/s1600/snap.png" height="226" width="320" /></a></span></span></div>
</div>
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625">
</span>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625"><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">In this and the next series of posts I’ll try to cover a few aspects of the development process that I think are worth sharing, especially those that may be of general programming interest. In fact, I’ll try not to make these notes inaccessible to someone who has never seen a Commodore 64 in his life, although I might be writing a couple of lines of assembler here and there (which I’ll try to explain in detail). Hopefully I’ll give some answers to those of you who are wondering how someone can even think of implementing something for a 33-year-old machine. And who knows? Maybe I’ll drag some of you into this madness. That would be great!</span></span></div>
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625">
</span>
<br />
<h1 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;">
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625">
<span style="font-family: 'Trebuchet MS'; font-size: 21px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">An appreciation of the limitations of the target platform</span></span></h1>
<span id="docs-internal-guid-b20a9084-08db-0f98-420f-5d630d44b625">
</span><span style="font-family: Arial; font-size: 15px; line-height: 1.38; white-space: pre-wrap;">It’s no mystery that the Commodore 64 comes with 64KB of RAM. This might sound like a silly amount of silicon to achieve anything sensible nowadays, but it was not back in the 80s. In 1982, when the machine came out, 64KB of RAM was “A LOT” of memory. In fact, it was the best selling point of the machine. This is a very important fact to consider, because we are targeting this specific machine, not a modern PC. An assembler instruction on the Commodore 64 takes 1 to 3 bytes. 64KB could host a good 30,000 “lines” of code if we were to use all of that space for this purpose. Furthermore, old machines in general, and the Commodore 64 in particular, were designed to make video game development (well... certain aspects of it) “easy”… Programming a ball that bounces around the screen, for example, is something that can be achieved very easily, and it really takes few bytes to be implemented. If you want to play a sound effect that resembles an explosion, that’s easy too. This is because the Commodore 64’s strongest asset were its custom chips that allowed programmers to achieve certain effects without much effort. The most famous and one of the most useful aces up the C64’s sleeve are its mighty hardware sprites. Let’s go back to the bouncing ball example.</span><span style="font-family: Arial; font-size: 15px; line-height: 1.38; white-space: pre-wrap;"> This is what we want to achieve:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div style="text-align: center;">
<br /></div>
<div id="container">
<style>
canvas {
border: 3px #CCC solid;
}
</style>
<canvas height="200" id="myCanvas" width="320"></canvas>
</div>
<script>
var mainCanvas = document.querySelector("#myCanvas");
var mainContext = mainCanvas.getContext("2d");
var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;
var xpos = 10
var ypos = 10
var xdir = 1
var ydir = 1
function drawCircle() {
mainContext.clearRect(0, 0, canvasWidth, canvasHeight);
// color in the background
mainContext.fillStyle = "#EEEEEE";
mainContext.fillRect(0, 0, canvasWidth, canvasHeight);
// draw the circle
mainContext.beginPath();
var radius = 10;
mainContext.arc(xpos, ypos, radius, 0, Math.PI * 2, false);
mainContext.closePath();
// color in the circle
mainContext.fillStyle = "#006699";
mainContext.fill();
xpos = xpos + xdir;
ypos = ypos + ydir;
if (xpos == canvasWidth-10 || xpos == 10)
{
xdir = -xdir;
}
if (ypos == canvasHeight-10 || ypos == 10)
{
ydir = -ydir;
}
var loopTimer = setTimeout('drawCircle()',20);
}
drawCircle();
</script>
</div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div style="text-align: center;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
</div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Now let’s write pseudocode to achieve this effect on a machine of that era with no Hardware sprites:</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">x = 0</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">y = 0</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">xdir = 1 //going right</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ydir = 1 //going down</span></div>
<b id="docs-internal-guid-b20a9084-087b-8c89-221f-ff353f302884" style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ball = {(x0,y0), (x1,y1), .. (xn,yn)} //a sequence of pixels making up the shape of the ball.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">while(true)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">foreach (pixel in ball)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">screen[x + pixel.x, y + pixel.y] =</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 72pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">backbuffer[x + pixel.x, y + pixel.y]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">x = x + xdir</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">y = y + ydir</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">if (x == 0 or x == rightborder)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">xdir = -xdir //bounce horizontally</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">if (y == 0 or y == bottomborder)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ydir = -ydir //bounce vertically</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">foreach (pixel in ball)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">screen[x+pixel.x, y+pixel.y] = ballcolour</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">somedelay()...</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<br /></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The first few lines set the ball position and direction. It will be initially positioned on the top-left corner of the screen. Each iteration of the loop will update the x and y position according to the direction. The problem here is what “update” means: </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There are three main steps here:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">erase the pixels at position (x,y) + offset for each pixel offset in ball</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">update the ball position (x,y)</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">set all the pixels at position (x,y) + offset for each pixel offset in ball</span></div>
</li>
</ol>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Surprisingly, this is not very different from what a modern PC does. The fact that machines and graphic cards are so fast now, makes this process totally invisible to the user, but those of you who witnessed the birth of the first GUIs, will definitely remember that moving windows around the screen triggered that weird erase/redraw effect. That’s exactly what went on in PCs in the early nineties: the user moving a window would result in all the pixels making up the window at the old position being erased (background is drawn) and then all the pixels being redrawn in the new position. It’s blazing fast nowadays, not so much 20 years ago. And indeed, things would get much more complicated when you had multiple objects that moved on the screen, which is always the case in games, unless you are dealing with something as simple as a bouncing ball. </span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">How would you achieve the same effect on the Commodore 64?</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The commodore 64 comes with 8 Hardware sprites. A sprite is a shape that can be defined by the user and positioned anywhere on the screen. What’s great about sprites is that they exist on a different layer than the background. This means that upon moving them, you don’t have to worry about the background. Let’s see what the bouncing ball pseudocode looks like on the C64.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">xdir = 1 //going right</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ydir = 1 //going down</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ball = {(x0,y0), (x1,y1), .. (xn,yn)} //a sequence of pixels making up the shape of the ball.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sprite0.pointer = ball</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sprite0.x = 0</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sprite0.y = 0</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sprite0.visible = true</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">while(true)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sprite0.x = sprite0.x + xdir</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sprite0.y = sprite0.y + ydir</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">if (sprite0.x == 0 or sprite0.x == rightborder)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">xdir = -xdir //bounce horizontally</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">if (sprite0.y == 0 or sprite0.y == bottomborder)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ydir = -ydir //bounce vertically</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">somedelay()</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">And that’s it. We basically set up the sprite number 0 to represent the ball, then in the main loop we just update the position of sprite0 and take care of the bouncing, and good old commodore 64 takes care of everything else.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We spoke about efficiency, which is clearly the most obvious advantage of this approach, but there’s so much more going on under the hood. The erase-redraw approach that other computers are forced to resort to implies that you have to store the background in memory twice. Once for the clean, unaltered background that you need to copy the pixels FROM when you are erasing, and once for the actual displayed version of it, with the ball and all the other objects overlayed. The concept of double buffering is an integral part of game development today (and even the Commodore 64 provides an elegant way to implement it), but you really don’t want to resort to it, unless you really need it, to limit the footprint of your graphics into memory.</span></div>
<div dir="ltr" style="margin-bottom: 0pt; margin-top: 0pt;">
<div style="line-height: 1.38;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In our specific example, besides the memory that goes to waste for something that simple, a lot of cpu power is wasted in erasing and drawing back the same thing over and over. There are a lot of tricks that programmers of other platforms came up with in the 80s to make this process as quick as possible, but the basic approach stays the same: you have to erase and redraw your objects.</span></div>
<span style="font-family: Arial;"><span style="font-size: 15px; line-height: 20.7000007629395px; white-space: pre-wrap;">This video shows the bouncing ball implemented on a C64. As you can see the background is completely unaffected by the ball movement.</span></span><br />
<br />
<video autoplay="" height="272" loop="" poster="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmTWhIMktjV1ZfZm8" width="384">
<source src="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmOUVxVWZIcU4xVk0" type="video/mp4"></source>
<source src="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmLW5vaE0tNjZ1NzA" type="video/webm"></source>
<source src="https://drive.google.com/uc?export=download&id=0BxTqsw9RMiBmTWlWMTRTRWRJb00" type="video/ogg"></source>
</video>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">This long preamble was needed to put things into perspective: The 16Kb limitation of the competition is indeed a tight constraint, but things are not so bad after all. There is a good hardware support for some common tasks that allows you to achieve interesting results with limited memory. The pure horsepower of the machine is ridiculous compared to your mobile (let alone your computer) but the sheer amount of “things” that go on on the display is very limited, and even when things get very busy, the number of pixels that the machine needs to shift at a certain moment in time is one order of magnitude smaller than those that your mobile has to move around, say, when you unlock the screen. Yes, you can’t play the latest tv show on your stock Commodore 64 (well, some people will disagree with this, but let’s not make these posts too hardcore, shall we?), but you can move objects on the display and play some sound without having to implement the basics of that.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">That’s why I’m not going to cover the aspects of how to squeeze a game in 16 kb on the Commodore 64, because in general that’s not really something to tell home about: there are literally thousands of videogames developed by pioneer coders in the 80s that are smaller than 16kb. Those unsung heroes deserve your respect because THEIR 16Kb creatures laid the foundation of the industry that brought you Halo, Fifa 2015 or whatever you guys are playing today. Nor I’m giving you an introduction to C64 coding, because there are dozen of tutorials on game programming for this machine (you’d be surprised!). Finally, I won’t introduce you to Commodore 64 programming at all, because </span><a href="https://www.youtube.com/watch?v=ZsRRCnque2E" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">this video</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> does it better than I could dream of and is well worth your “I’m not slacking, my code is compiling”-time today. Check it out!</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">What the next posts will cover are those aspects of the development of P0 Snake that challenged and intrigued me the most. The hurdles I had to come to term with and the makeshifts that allowed me to overcome them. It’ll be about how we can conjugate modern tools and knowledge with vintage technology to craft something beautifully old. It’s not going to be a tutorial (I would never be that arrogant), nor a lesson in data compression (again). It’s just the way I did it, which is not necessarily the best way of doing it. But, heck, it worked!</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Stay tuned.</span></div>
<br />tonysavonhttp://www.blogger.com/profile/18275184003580732693noreply@blogger.com1