├── test ├── 1.php └── 1.xml ├── LICENSE.md ├── benchmark ├── index.php └── sample_100.xml ├── README.md └── xml.php /test/1.php: -------------------------------------------------------------------------------- 1 | data); 7 | 8 | ?> -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Marat A. Denenberg 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /benchmark/index.php: -------------------------------------------------------------------------------- 1 | children() as $elementName => $node) 8 | { 9 | $nextIdx = count($arr); 10 | $arr[$nextIdx] = [ 11 | '@name' => strtolower((string)$elementName), 12 | '@attributes' => [], 13 | '@children' => [], 14 | '@text' => trim((string)$node) 15 | ]; 16 | foreach($node->attributes() as $attributeName => $attributeValue) 17 | { 18 | $arr[$nextIdx]['@attributes'][strtolower( 19 | trim((string)$attributeName) 20 | )] = trim((string)$attributeValue); 21 | } 22 | convertSimpleXmlObjToArr($node, $arr[$nextIdx]['@children']); 23 | } 24 | } 25 | 26 | $path = dirname(__FILE__) . DIRECTORY_SEPARATOR; 27 | foreach( 28 | [ 29 | 'simplexml' => [ 30 | '100' => "{$path}sample_100.xml", 31 | '10000' => "{$path}sample_10000.xml" 32 | ], 33 | 'xml' => [ 34 | '100' => "{$path}sample_100.xml", 35 | '10000' => "{$path}sample_10000.xml" 36 | ] 37 | ] 38 | as 39 | $type => $files 40 | ) 41 | { 42 | foreach($files as $lines => $file) 43 | { 44 | $samples = []; 45 | for($i = 0; $i < 32; $i++) 46 | { 47 | $start = microtime(true); 48 | switch($type) 49 | { 50 | case 'simplexml': 51 | $sxe = new SimpleXMLElement($file, null, true); 52 | $arr = []; 53 | convertSimpleXmlObjToArr($sxe, $arr); 54 | unset($sxe, $arr); 55 | break; 56 | 57 | case 'xml': 58 | $xml = new xml($file); 59 | $arr = $xml->data; 60 | unset($xml, $arr); 61 | break; 62 | } 63 | $samples[] = microtime(true) - $start; 64 | } 65 | echo "{$type}: Parsed {$lines} line xml file in " . 66 | number_format((array_sum($samples) / 32), 4) . 67 | " seconds.\n"; 68 | } 69 | } 70 | 71 | ?> -------------------------------------------------------------------------------- /benchmark/sample_100.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Belgian Waffles 5 | $5.95 6 | Two of our famous Belgian Waffles with plenty of real maple syrup. 7 | 8 | 9 | 10 | Strawberry Belgian Waffles 11 | $7.95 12 | Light Belgian waffles covered with strawberries and whipped cream. 13 | 900 14 | 15 | 16 | Waffles 17 | 18 | 19 | Belgian Waffles 20 | $5.95 21 | Two of our famous Belgian Waffles with plenty of real maple syrup. 22 | 23 | 24 | 25 | Strawberry Belgian Waffles 26 | $7.95 27 | Light Belgian waffles covered with strawberries and whipped cream. 28 | 900 29 | 30 | 31 | Waffles 32 | 33 | 34 | Belgian Waffles 35 | $5.95 36 | Two of our famous Belgian Waffles with plenty of real maple syrup. 37 | 38 | 39 | 40 | Strawberry Belgian Waffles 41 | $7.95 42 | Light Belgian waffles covered with strawberries and whipped cream. 43 | 900 44 | 45 | 46 | Waffles 47 | 48 | 49 | Belgian Waffles 50 | $5.95 51 | Two of our famous Belgian Waffles with plenty of real maple syrup. 52 | 53 | 54 | 55 | Strawberry Belgian Waffles 56 | $7.95 57 | Light Belgian waffles covered with strawberries and whipped cream. 58 | 900 59 | 60 | 61 | Waffles 62 | 63 | 64 | Belgian Waffles 65 | $5.95 66 | Two of our famous Belgian Waffles with plenty of real maple syrup. 67 | 68 | 69 | 70 | Strawberry Belgian Waffles 71 | $7.95 72 | Light Belgian waffles covered with strawberries and whipped cream. 73 | 900 74 | 75 | 76 | Waffles 77 | 78 | 79 | Belgian Waffles 80 | $5.95 81 | Two of our famous Belgian Waffles with plenty of real maple syrup. 82 | 83 | 84 | 85 | Strawberry Belgian Waffles 86 | $7.95 87 | Light Belgian waffles covered with strawberries and whipped cream. 88 | 900 89 | 90 | 91 | Waffles 92 | 93 | 94 | Belgian Waffles 95 | $5.95 96 | Two of our famous Belgian Waffles with plenty of real maple syrup. 97 | 98 | 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP XML Parser 2 | 3 | A PHP XML parser class that provides an easy way to convert XML into native PHP 4 | arrays and back again. It has no dependencies on any external libraries or 5 | extensions bundled with PHP. The entire parser is concisely written in PHP. 6 | 7 | This project is actively maintained. It is used in our production code. If you 8 | spot an issue, please let us know through the Issues section on our Github 9 | project page: https://github.com/revnode/xml/issues 10 | 11 | ## Why 12 | 13 | As XML becomes less popular, the need for a parser moves from constant to 14 | infrequent. It makes little sense to keep a parser resident in memory at all times 15 | for functionality that might be used once every few days. 16 | 17 | For example, just to get SimpleXML going, you will need to have the libxml2 18 | library installed on your system. You will need xml, libxml, and simplexml 19 | extensions installed for PHP. You will need to keep all those extensions in 20 | memory for each request. 21 | 22 | In contrast, this simple parser is less than 500 lines of code and is only 23 | loaded when you need it. It has no dependencies, no required libraries or 24 | extensions, and will work on any modern PHP installation. The price you pay for 25 | that convenience is speed. It is much slower than SimpleXML. See the benchmarking 26 | section for details. 27 | 28 | In short, this project makes sense for those who want to simplify their PHP 29 | install and use, have a need for a simple XML parser, but don't much care 30 | about speed. 31 | 32 | ## Requirements 33 | 34 | PHP 5.4.0+ 35 | 36 | ## Install 37 | 38 | Just place the xml.php file in a convenient location and include it in your 39 | code. 40 | 41 | ## Design Goals 42 | 43 | * Zero dependencies on external libraries or PHP extensions. 44 | * Provide a parser from and to the XML standard. 45 | * Provide support for the most commonly used parts of the XML standard. 46 | * Maintain a minimal memory footprint during operation, even for large XML files. 47 | * Maintain a codebase that is less than 1000 lines. Currently at less than 500 lines. 48 | 49 | ## Usage 50 | 51 | ### XML String to PHP Array 52 | 53 | ```php 54 | 59 | 60 | 61 | Waffles 62 | 63 | '); 64 | var_dump($xml->data); 65 | 66 | ?> 67 | ``` 68 | 69 | ### XML File to PHP Array 70 | 71 | ```php 72 | data); 78 | 79 | ?> 80 | ``` 81 | 82 | ### PHP Array to XML String 83 | 84 | ```php 85 | [ 91 | [ 92 | 'food' => [ 93 | 'name' => 'Waffles' 94 | ] 95 | ] 96 | ] 97 | ]); 98 | echo $xml; 99 | 100 | ?> 101 | ``` 102 | 103 | ## Benchmarks 104 | 105 | A benchmark to measure memory performance for large xml files is on the todo list. 106 | 107 | The following is the result of the benchmark code executing. The code is executed 108 | 32 times and then averaged. The code was run on an Intel(R) Atom(TM) CPU D510 109 | @ 1.66GHz with 4GB of RAM. 110 | 111 | ``` 112 | simplexml: Parsed 100 line xml file in 0.0033 seconds. 113 | simplexml: Parsed 10000 line xml file in 2.0063 seconds. 114 | xml: Parsed 100 line xml file in 0.0251 seconds. 115 | xml: Parsed 10000 line xml file in 11.8074 seconds. 116 | ``` 117 | 118 | For small files, the simplexml parser is almost 8 times faster. For larger files, 119 | it is 6 times faster. 120 | 121 | This is a signficant performance difference. However, if you're using the code 122 | on a file that's not that large, even with the performance degradation, the job 123 | will finish within a fraction of a second. 124 | 125 | ## Tests 126 | 127 | None, for now. 128 | 129 | ## License 130 | 131 | MIT, see LICENSE.md -------------------------------------------------------------------------------- /xml.php: -------------------------------------------------------------------------------- 1 | xml = ( 25 | ($data[0] == DIRECTORY_SEPARATOR && file_exists($data)) 26 | ? 27 | file_get_contents($data) 28 | : 29 | $data 30 | ); 31 | if(!empty($this->xml) && $this->xml[0] == '<') 32 | { 33 | $this->parse(); 34 | } 35 | break; 36 | 37 | case 'array': 38 | $this->data = $data; 39 | break; 40 | } 41 | } 42 | 43 | public function __toString() 44 | { 45 | return ( 46 | (isset($this->declaration)&&trim($this->declaration)) 47 | ? 48 | "declaration}?>" 49 | : 50 | '' 51 | ) . $this->traverse($this->data); 52 | } 53 | 54 | private function traverse($node) 55 | { 56 | $xml = ''; 57 | $attributes = ''; 58 | if(count($node) > 1) 59 | { 60 | foreach(array_slice($node, 1) as $key => $value) 61 | { 62 | $attributes .= ' ' . substr($key, 1) . '="' . $value . '"'; 63 | } 64 | } 65 | foreach(array_slice($node, 0, 1) as $tag => $data) 66 | { 67 | switch(gettype($data)) 68 | { 69 | case 'array': 70 | $xml .= "<{$tag}{$attributes}>"; 71 | if($this->is_assoc($data)) 72 | { 73 | foreach($data as $child) 74 | { 75 | $xml .= $this->traverse($child); 76 | } 77 | } 78 | else 79 | { 80 | $xml .= $this->traverse($data); 81 | } 82 | $xml .= ""; 83 | break; 84 | 85 | case 'NULL': 86 | $xml .= "<{$tag}{$attributes} />"; 87 | break; 88 | 89 | default: 90 | $xml .= "<{$tag}{$attributes}>{$data}"; 91 | break; 92 | } 93 | } 94 | return $xml; 95 | } 96 | 97 | private function is_assoc($array) 98 | { 99 | return ( 100 | count( 101 | array_filter( 102 | array_keys($array), 103 | 'is_string' 104 | ) 105 | ) > 0 106 | ); 107 | } 108 | 109 | private function parse() 110 | { 111 | $this->xml = str_replace( 112 | "\t", 113 | ' ', 114 | $this->xml 115 | ); 116 | 117 | $this->stack[] =& $this->data; 118 | 119 | for( 120 | $length = strlen($this->xml); 121 | $this->index < $length; 122 | $this->index++ 123 | ) 124 | { 125 | switch($this->xml[$this->index]) 126 | { 127 | case '<': 128 | switch($this->xml[$this->index + 1]) 129 | { 130 | case '?': 131 | $this->index += 2; 132 | $this->syntax = 'syntax_declaration'; 133 | break; 134 | 135 | case '/': 136 | $this->index += 2; 137 | $this->tag_name = ''; 138 | $this->syntax = 'syntax_tag_back_start'; 139 | break; 140 | 141 | default: 142 | $this->index += 1; 143 | $this->tag_name = $this->tag_value = ''; 144 | $this->attributes = []; 145 | $this->syntax = 'syntax_tag_front_start'; 146 | break; 147 | } 148 | break; 149 | 150 | case '/': 151 | switch($this->xml[$this->index + 1]) 152 | { 153 | case '>': 154 | $this->index += 1; 155 | if($this->syntax == 'syntax_attribute_name') 156 | { 157 | $this->xml = substr($this->xml, $this->index); 158 | $this->index = 0; 159 | $length = strlen($this->xml); 160 | $this->syntax = 'syntax_tag_short'; 161 | } 162 | else 163 | { 164 | $this->syntax = 'syntax_tag_back_end'; 165 | } 166 | break; 167 | } 168 | break; 169 | 170 | case '>': 171 | switch($this->syntax) 172 | { 173 | case 'syntax_tag_front_start': 174 | case 'syntax_attribute_name': 175 | $this->syntax = 'syntax_tag_front_end'; 176 | break; 177 | 178 | default: 179 | $this->xml = substr($this->xml, $this->index); 180 | $this->index = 0; 181 | $length = strlen($this->xml); 182 | $this->syntax = 'syntax_tag_back_end'; 183 | break; 184 | } 185 | break; 186 | 187 | case "\n": 188 | $this->line++; 189 | break; 190 | } 191 | 192 | $this->{$this->syntax}(); 193 | } 194 | 195 | unset($this->xml); 196 | } 197 | 198 | // ### Iterator: foreach access ### 199 | 200 | public function rewind() 201 | { 202 | reset($this->data); 203 | } 204 | 205 | public function current() 206 | { 207 | return current($this->data); 208 | } 209 | 210 | public function key() 211 | { 212 | return key($this->data); 213 | } 214 | 215 | public function next() 216 | { 217 | return next($this->data); 218 | } 219 | 220 | public function valid() 221 | { 222 | $key = key($this->data); 223 | return ($key !== null && $key !== false); 224 | } 225 | 226 | // ### ArrayAccess: key/value access ### 227 | 228 | public function offsetSet($offset, $value) 229 | { 230 | if(is_null($offset)) 231 | { 232 | $this->data[] = $value; 233 | } 234 | else 235 | { 236 | $this->data[$offset] = $value; 237 | } 238 | } 239 | 240 | public function offsetExists($offset) 241 | { 242 | return isset($this->data[$offset]); 243 | } 244 | 245 | public function offsetUnset($offset) 246 | { 247 | unset($this->data[$offset]); 248 | } 249 | 250 | public function offsetGet($offset) 251 | { 252 | return ( 253 | isset($this->data[$offset]) 254 | ? 255 | $this->data[$offset] 256 | : 257 | null 258 | ); 259 | } 260 | 261 | // ### START ### Declaration ### 262 | 263 | public function version() 264 | { 265 | return ( 266 | preg_match('#version\="(.*)"#U', $this->declaration, $match) 267 | ? 268 | $match[1] 269 | : 270 | '1.0' 271 | ); 272 | } 273 | 274 | public function encoding() 275 | { 276 | return ( 277 | preg_match('#encoding\="(.*)"#U', $this->declaration, $match) 278 | ? 279 | $match[1] 280 | : 281 | 'utf-8' 282 | ); 283 | } 284 | 285 | private function syntax_declaration() 286 | { 287 | if( 288 | $this->xml[$this->index] == '?' 289 | && 290 | $this->xml[$this->index + 1] == '>' 291 | ) 292 | { 293 | $this->index++; 294 | $this->syntax = 'syntax_tag_value'; 295 | } 296 | else 297 | { 298 | $this->declaration .= $this->xml[$this->index]; 299 | } 300 | } 301 | 302 | // ### END ### Declaration ### 303 | 304 | private function syntax_error() 305 | { 306 | error_log("Syntax error in XML data. Please check line # {$this->line}."); 307 | } 308 | 309 | private function syntax_tag_front_start() 310 | { 311 | switch($this->xml[$this->index]) 312 | { 313 | case ' ': 314 | $this->syntax = 'syntax_attribute_name'; 315 | $this->attribute_name = $this->attribute_value = ''; 316 | break; 317 | 318 | default: 319 | $this->tag_name .= $this->xml[$this->index]; 320 | break; 321 | } 322 | } 323 | 324 | private function syntax_tag_front_end() 325 | { 326 | $node = []; 327 | $node[$this->tag_name] = []; 328 | if(!empty($this->attributes)) 329 | { 330 | foreach($this->attributes as $key => $value) 331 | { 332 | $node["@{$key}"] = $value; 333 | } 334 | } 335 | 336 | $current =& $this->stack[count($this->stack) - 1]; 337 | if(empty($current)) 338 | { 339 | $current = $node; 340 | $this->stack[] =& $current[$this->tag_name]; 341 | } 342 | else 343 | { 344 | if($this->is_assoc($current)) 345 | { 346 | $current = [$current, $node]; 347 | } 348 | else 349 | { 350 | $current[] = $node; 351 | } 352 | $this->stack[] =& $current[count($current) - 1][$this->tag_name]; 353 | } 354 | 355 | $this->syntax = 'syntax_tag_value'; 356 | } 357 | 358 | private function syntax_tag_short() 359 | { 360 | $this->syntax_tag_front_end(); 361 | $this->syntax_tag_back_end(); 362 | } 363 | 364 | private function syntax_tag_back_start() 365 | { 366 | $this->tag_name .= $this->xml[$this->index]; 367 | } 368 | 369 | private function syntax_tag_back_end() 370 | { 371 | $child =& $this->stack[count($this->stack) - 1]; 372 | array_pop($this->stack); 373 | 374 | $last = count($this->stack) - 1; 375 | if( 376 | isset($this->stack[$last][$this->tag_name]) 377 | || 378 | isset(end($this->stack[$last])[$this->tag_name]) 379 | ) 380 | { 381 | if(empty($child)) 382 | { 383 | $child = ( 384 | ( 385 | ($this->tag_value = trim($this->tag_value)) 386 | && 387 | $this->tag_value != '' 388 | ) 389 | ? 390 | $this->tag_value 391 | : 392 | null 393 | ); 394 | } 395 | $this->tag_value = ''; 396 | $this->syntax = 'syntax_tag_value'; 397 | } 398 | else 399 | { 400 | $this->syntax_error(); 401 | } 402 | } 403 | 404 | private function syntax_tag_value() 405 | { 406 | $this->tag_value .= $this->xml[$this->index]; 407 | } 408 | 409 | private function syntax_attribute_name() 410 | { 411 | switch($this->xml[$this->index]) 412 | { 413 | case '=': 414 | case ' ': 415 | break; 416 | 417 | case '"': 418 | $this->syntax = 'syntax_attribute_value'; 419 | break; 420 | 421 | default: 422 | $this->attribute_name .= $this->xml[$this->index]; 423 | break; 424 | } 425 | } 426 | 427 | private function syntax_attribute_value() 428 | { 429 | switch($this->xml[$this->index]) 430 | { 431 | case '"': 432 | $this->syntax = 'syntax_attribute_end'; 433 | $this->index--; 434 | break; 435 | 436 | default: 437 | $this->attribute_value .= $this->xml[$this->index]; 438 | break; 439 | } 440 | } 441 | 442 | private function syntax_attribute_end() 443 | { 444 | $this->attributes[$this->attribute_name] = $this->attribute_value; 445 | $this->syntax = 'syntax_tag_front_start'; 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /test/1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Great conversations: the pianists 10 | 11 | 12 | Istomin, Eugene 13 | 14 | Host 15 | 16 | 17 | 18 | Bronfman, Yefim 19 | 20 | Interviewee 21 | 22 | 23 | 24 | Ax, Emanuel 25 | 26 | Interviewee 27 | 28 | 29 | 30 | Fleisher, Leon 31 | 32 | Interviewee 33 | 34 | 35 | 36 | Rosen, Charles 37 | 38 | Interviewee 39 | 40 | 41 | 42 | Graffman, Gary 43 | 44 | Interviewee 45 | 46 | 47 | 48 | Rosen, Peter 49 | 50 | Producer 51 | 52 | 53 | moving image 54 | 55 | 56 | New York 57 | 58 | Peter Rosen Productions, Inc. 59 | 2005 60 | monographic 61 | 62 | 63 | videorecording 64 | access 65 | 1 digibeta videotape; duration: 60 min., 20 sec. 66 | reformatted digital 67 | 68 | adult 69 | Eugene Istomin and his fellow pianists Emanuel Ax, Yefim Bronfman, Leon Fleisher, Gary Graffman, and Charles Rosen offer insights into the world of the piano as they discuss performance technique and adventures in recording, make humorous observations about their colleagues, and tell inside jokes about great musical icons. The conversations were filmed in the elegant surroundings of the Members’ Room in the Library of Congress’ Thomas Jefferson Building. 70 | Program is early edited version and contains the title "The green room," which was later changed to "Great conversations." 71 | "Program one." 72 | In opening credits: "From the Library of Congress, Washington, D.C." 73 | Additional credits: For the Music Division of the Library of Congress: Jon Newsom, chief; Jan Lauridsen, Assist. Chief, Ruth Foss, Program specialist. 74 | Library of Congress extended version. 75 | Copyright Library of Congress. This program was made possible through the courtesy of Eugene Istomin. Artists' appearances are courtesy of themselves: Emanuel Ax, Yefim Bronfman, Leon Fleisher, Gary Graffman, and Charles Rosen. 76 | Copyright Library of Congress. This program was made possible through the courtesy of Eugene Istomin. Artists' appearances are courtesy of themselves: Emanuel Ax, Yefim Bronfman, Leon Fleisher, Gary Graffman, and Charles Rosen. 77 | 78 | 79 | Opening credits; and The early years 80 | 81 | moving image 82 | 83 | 84 | New York 85 | 86 | Peter Rosen Productions, Inc. 87 | 2005 88 | 89 | 90 | video 91 | access 92 | 11:53 93 | reformatted digital 94 | 95 | Istomin introduces his guests and initiates a conversation on the influences that made each become a pianist. Rosen and Graffman share anecdotes about playing for Leopold Godowsky. 96 | 97 | 98 | 99 | Roots 100 | 101 | moving image 102 | 103 | 104 | New York 105 | 106 | Peter Rosen Productions, Inc. 107 | 2005 108 | 109 | 110 | video 111 | access 112 | 7:33 113 | reformatted digital 114 | 115 | Istomin and his guests talk about the roots of various piano traditions and national schools of piano technique. 116 | 117 | 118 | 119 | Performance 120 | 121 | moving image 122 | 123 | 124 | New York 125 | 126 | Peter Rosen Productions, Inc. 127 | 2005 128 | 129 | 130 | video 131 | access 132 | 9:55 133 | reformatted digital 134 | 135 | Istomin and his guests share opinions on twentieth-century pianists who returned to the Urtext as a basis for interpretation as well views on those who exercise an excess of bodily motion in performance. 136 | 137 | 138 | 139 | Tradition 140 | 141 | moving image 142 | 143 | 144 | New York 145 | 146 | Peter Rosen Productions, Inc. 147 | 2005 148 | 149 | 150 | video 151 | access 152 | 8:34 153 | reformatted digital 154 | 155 | Istomin reminisces about the origins of the first recording Sergei Rachmaninoff’s Piano Concerto No. 4 in D minor, Op. 40. Fleischer and Graffman talk about pedagogy. 156 | 157 | 158 | 159 | Interpretation 160 | 161 | moving image 162 | 163 | 164 | New York 165 | 166 | Peter Rosen Productions, Inc. 167 | 2005 168 | 169 | 170 | video 171 | access 172 | 7:58 173 | reformatted digital 174 | 175 | Istomin and his guests discuss what it means to interpret a piece of music. 176 | 177 | 178 | 179 | Tone 180 | 181 | moving image 182 | 183 | 184 | New York 185 | 186 | Peter Rosen Productions, Inc. 187 | 2005 188 | 189 | 190 | video 191 | access 192 | 11:25 193 | reformatted digital 194 | 195 | Istomin plays a portion of Rachmaninoff’s Piano Concerto No. 4, which leads to a discussion of tone. 196 | 197 | 198 | 199 | Epilogue; and Closing credits 200 | 201 | moving image 202 | 203 | 204 | New York 205 | 206 | Peter Rosen Productions, Inc. 207 | 2005 208 | 209 | 210 | video 211 | access 212 | 3:02 213 | reformatted digital 214 | 215 | Istomin provides an epilogue to the discussions by paying homage to other great pianists, including Ignace Paderweski, Artur Schnabel, Rudolf Serkin, Claudio Arrau, and Dame Myra Hess. 216 | 217 | 200031106 218 | 23601 219 | 220 | Music Division 221 | DLC 222 | 223 | 224 | IHAS 225 | 051125 226 | loc.natlib.ihas.200031106 227 | 228 | greatconv 229 | Great Conversations in Music 230 | 231 | 232 | Great Conversations in Music 233 | 234 | 235 | http://lcweb2.loc.gov/diglib/ihas/html/greatconversations/great-home.html 236 | 237 | 238 | 239 | 240 | Performing Arts Encyclopedia 241 | 242 | 243 | http://www.loc.gov/performingarts 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | --------------------------------------------------------------------------------