├── .gitignore ├── waveform.css ├── LICENSE ├── README.md └── php-waveform-svg.php /.gitignore: -------------------------------------------------------------------------------- 1 | lame* 2 | *.wav 3 | *.mp3 -------------------------------------------------------------------------------- /waveform.css: -------------------------------------------------------------------------------- 1 | /** 2 | * RECT FOR BACKGROUND COLOUR 3 | */ 4 | 5 | rect { 6 | fill: rgb(255,0,0); 7 | } 8 | 9 | /** 10 | * WAVEFORM LINE COLOUR 11 | */ 12 | 13 | line { 14 | stroke: rgb(0,0,0); 15 | stroke-width: 1px; 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Andrew Freiday 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP MP3 SVG Waveform Generator 2 | === 3 | 4 | ### About 5 | 6 | Generates SVG waveform images for a given MP3 file by converting it to WAV and processing amplitude points and plotting them as an SVG XML file. 7 | 8 | This code was adapted by [Andrew Freiday](http://andrewfreiday.com). 9 | 10 | Based on the [PHP MP3 Waveform Generator](https://github.com/afreiday/php-waveform-png). 11 | 12 | ### Requirements 13 | 14 | - PHP 5+ 15 | - LAME MP3 Encoder (Windows build available from http://www.rarewares.org/mp3-lame-bundle.php) 16 | - Web server (Apache, etc -- unless modified to run as a command line script) 17 | 18 | ### Usage/Installation 19 | 20 | - Copy to a location on your web server 21 | - Ensure that the 'lame' command is accessible and executable from that directory (for Windows systems, place the downloaded .exe and .dll as linked above into that same directory, or modify the script) 22 | - Ensure the directory support write persmissions, or specify an alternate temporary output directory that does 23 | - Launch the script in your browser (e.g. http://localhost/php-waveform-svg.php) and upload your MP3 24 | - Modify waveform.css to change the display colours/properties of your SVG waveform 25 | 26 | ### Image generation options 27 | 28 | - The "stereo waveform" option allows you to generate a single SVG image with both left and right audio channels separately 29 | 30 | ### Other notes 31 | 32 | The detail of the waveform can be adjusted by modifying the DETAIL constant at the top of the script. The lower the number, the less data points are skipped, thus resulting in higher detail waveform. 33 | 34 | The most time-consuming factor of the script is the LAME encoder, which first converts the uploaded MP3 into a 16 bit, mono, 8 KHz MP3 and finally to a WAV audio file. Unfortunately this doesn't seem to be possible to perform this in one step with the LAME encoder. If this script is to be modified to generate waveforms on the fly for the same MP3 multiple times, I recommend either storing a copy of the final WAV file or modifying the script to keep a dataset of the amplitude points so that the waveform can be redrawn much more quickly. 35 | 36 | ### License 37 | 38 | Please see the LICENSE file. -------------------------------------------------------------------------------- /php-waveform-svg.php: -------------------------------------------------------------------------------- 1 | \n"; 69 | $svg .= "\n"; 70 | $svg .= "\n"; 71 | $svg .= "\n"; 72 | // rect for background color 73 | $svg .= "\n"; 74 | 75 | $y_offset = floor(1 / sizeof($wavs_to_process) * 100); 76 | 77 | // process each wav individually 78 | for($wav = 1; $wav <= sizeof($wavs_to_process); $wav++) { 79 | 80 | $svg .= ""; 81 | 82 | $filename = $wavs_to_process[$wav - 1]; 83 | 84 | /** 85 | * Below as posted by "zvoneM" on 86 | * http://forums.devshed.com/php-development-5/reading-16-bit-wav-file-318740.html 87 | * as findValues() defined above 88 | * Translated from Croation to English - July 11, 2011 89 | */ 90 | $handle = fopen($filename, "r"); 91 | // wav file header retrieval 92 | $heading[] = fread($handle, 4); 93 | $heading[] = bin2hex(fread($handle, 4)); 94 | $heading[] = fread($handle, 4); 95 | $heading[] = fread($handle, 4); 96 | $heading[] = bin2hex(fread($handle, 4)); 97 | $heading[] = bin2hex(fread($handle, 2)); 98 | $heading[] = bin2hex(fread($handle, 2)); 99 | $heading[] = bin2hex(fread($handle, 4)); 100 | $heading[] = bin2hex(fread($handle, 4)); 101 | $heading[] = bin2hex(fread($handle, 2)); 102 | $heading[] = bin2hex(fread($handle, 2)); 103 | $heading[] = fread($handle, 4); 104 | $heading[] = bin2hex(fread($handle, 4)); 105 | 106 | // wav bitrate 107 | $peek = hexdec(substr($heading[10], 0, 2)); 108 | $byte = $peek / 8; 109 | 110 | // checking whether a mono or stereo wav 111 | $channel = hexdec(substr($heading[6], 0, 2)); 112 | 113 | $ratio = ($channel == 2 ? 40 : 80); 114 | 115 | // start putting together the initial canvas 116 | // $data_size = (size_of_file - header_bytes_read) / skipped_bytes + 1 117 | $data_size = floor((filesize($filename) - 44) / ($ratio + $byte) + 1); 118 | $data_point = 0; 119 | 120 | while(!feof($handle) && $data_point < $data_size){ 121 | if ($data_point++ % DETAIL == 0) { 122 | $bytes = array(); 123 | 124 | // get number of bytes depending on bitrate 125 | for ($i = 0; $i < $byte; $i++) 126 | $bytes[$i] = fgetc($handle); 127 | 128 | switch($byte){ 129 | // get value for 8-bit wav 130 | case 1: 131 | $data = findValues($bytes[0], $bytes[1]); 132 | break; 133 | // get value for 16-bit wav 134 | case 2: 135 | if(ord($bytes[1]) & 128) 136 | $temp = 0; 137 | else 138 | $temp = 128; 139 | $temp = chr((ord($bytes[1]) & 127) + $temp); 140 | $data = floor(findValues($bytes[0], $temp) / 256); 141 | break; 142 | } 143 | 144 | // skip bytes for memory optimization 145 | fseek($handle, $ratio, SEEK_CUR); 146 | 147 | // draw this data point 148 | // data values can range between 0 and 255 149 | $x1 = $x2 = number_format($data_point / $data_size * 100, 2); 150 | $y1 = number_format($data / 255 * 100, 2); 151 | $y2 = 100 - $y1; 152 | // don't bother plotting if it is a zero point 153 | if ($y1 != $y2) 154 | $svg .= ""; 155 | 156 | } else { 157 | // skip this one due to lack of detail 158 | fseek($handle, $ratio + $byte, SEEK_CUR); 159 | } 160 | } 161 | 162 | $svg .= "\n"; 163 | 164 | // close and cleanup 165 | fclose($handle); 166 | 167 | // delete the processed wav file 168 | unlink($filename); 169 | 170 | } 171 | 172 | $svg .= "\n"; 173 | 174 | header("Content-Type: image/svg+xml"); 175 | 176 | print $svg; 177 | 178 | } else { 179 | 180 | ?> 181 | 182 |
" enctype="multipart/form-data"> 183 | 184 |

MP3 File:
185 |

186 | 187 |

Stereo waveform?

188 | 189 |

190 | 191 |
192 | 193 |