![]() |
Mixxx
|
00001 /**************************************************************************** 00002 encodervorbis.cpp - vorbis encoder for mixxx 00003 ------------------- 00004 copyright : (C) 2007 by Wesley Stessens 00005 (C) 1994 by Xiph.org (encoder example) 00006 (C) 1994 Tobias Rafreider (shoutcast and recording fixes) 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 /* 00019 Okay, so this is the vorbis encoder class... 00020 It's a real mess right now. 00021 00022 When I get around to cleaning things up, 00023 I'll probably make an Encoder base class, 00024 so we can easily add in an EncoderLame (or something) too. 00025 00026 A lot of stuff has been stolen from: 00027 http://svn.xiph.org/trunk/vorbis/examples/encoder_example.c 00028 */ 00029 00030 // TODO: MORE ERROR CHECKING EVERYWHERE (encoder and shoutcast classes) 00031 00032 #include "recording/encodervorbis.h" 00033 00034 #include <stdlib.h> // needed for random num gen 00035 #include <time.h> // needed for random num gen 00036 #include <string.h> // needed for memcpy 00037 #include <QDebug> 00038 00039 #include "engine/engineabstractrecord.h" 00040 #include "controlobjectthreadmain.h" 00041 #include "controlobject.h" 00042 #include "playerinfo.h" 00043 #include "trackinfoobject.h" 00044 #include "errordialoghandler.h" 00045 00046 // Constructor 00047 EncoderVorbis::EncoderVorbis(EngineAbstractRecord *engine) { 00048 m_bStreamInitialized = false; 00049 m_pEngine = engine; 00050 m_metaDataTitle = NULL; 00051 m_metaDataArtist = NULL; 00052 m_metaDataAlbum = NULL; 00053 m_pMetaData = TrackPointer(NULL); 00054 m_samplerate = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Master]", "samplerate"))); 00055 } 00056 00057 // Destructor //call flush before any encoder gets deleted 00058 EncoderVorbis::~EncoderVorbis() { 00059 if (m_bStreamInitialized) { 00060 ogg_stream_clear(&m_oggs); 00061 vorbis_block_clear(&m_vblock); 00062 vorbis_dsp_clear(&m_vdsp); 00063 vorbis_comment_clear(&m_vcomment); 00064 vorbis_info_clear(&m_vinfo); 00065 } 00066 delete m_samplerate; 00067 } 00068 //call sendPackages() or write() after 'flush()' as outlined in engineshoutcast.cpp 00069 void EncoderVorbis::flush() { 00070 vorbis_analysis_wrote(&m_vdsp, 0); 00071 writePage(); 00072 } 00073 00074 /* 00075 Get new random serial number 00076 -> returns random number 00077 */ 00078 int EncoderVorbis::getSerial() 00079 { 00080 static int prevSerial = 0; 00081 int serial = rand(); 00082 while (prevSerial == serial) 00083 serial = rand(); 00084 prevSerial = serial; 00085 qDebug() << "RETURNING SERIAL " << serial; 00086 return serial; 00087 } 00088 00089 void EncoderVorbis::writePage() { 00090 00091 /* 00092 * Vorbis streams begin with three headers; the initial header (with 00093 * most of the codec setup parameters) which is mandated by the Ogg 00094 * bitstream spec. The second header holds any comment fields. The 00095 * third header holds the bitstream codebook. We merely need to 00096 * make the headers, then pass them to libvorbis one at a time; 00097 * libvorbis handles the additional Ogg bitstream constraints 00098 */ 00099 00100 00101 //Write header only once after stream has been initalized 00102 int result; 00103 if(m_header_write){ 00104 while (1) { 00105 result = ogg_stream_flush(&m_oggs, &m_oggpage); 00106 if (result==0) break; 00107 m_pEngine->write(m_oggpage.header, m_oggpage.body, m_oggpage.header_len, m_oggpage.body_len); 00108 } 00109 m_header_write = false; 00110 } 00111 00112 while (vorbis_analysis_blockout(&m_vdsp, &m_vblock) == 1) { 00113 vorbis_analysis(&m_vblock, 0); 00114 vorbis_bitrate_addblock(&m_vblock); 00115 while (vorbis_bitrate_flushpacket(&m_vdsp, &m_oggpacket)) { 00116 // weld packet into bitstream 00117 ogg_stream_packetin(&m_oggs, &m_oggpacket); 00118 // write out pages 00119 int eos = 0; 00120 while (!eos) { 00121 int result = ogg_stream_pageout(&m_oggs, &m_oggpage); 00122 if (result == 0) break; 00123 m_pEngine->write(m_oggpage.header, m_oggpage.body, m_oggpage.header_len, m_oggpage.body_len); 00124 if (ogg_page_eos(&m_oggpage)) eos = 1; 00125 } 00126 } 00127 } 00128 } 00129 00130 void EncoderVorbis::encodeBuffer(const CSAMPLE *samples, const int size) { 00131 float **buffer; 00132 int i; 00133 00134 buffer = vorbis_analysis_buffer(&m_vdsp, size); 00135 00136 // Deinterleave samples 00137 for (i = 0; i < size/2; ++i) 00138 { 00139 buffer[0][i] = samples[i*2]/32768.f; 00140 buffer[1][i] = samples[i*2+1]/32768.f; 00141 } 00143 vorbis_analysis_wrote(&m_vdsp, i); 00145 writePage(); 00146 } 00147 00148 /* Originally called from engineshoutcast.cpp to update metadata information 00149 * when streaming, however, this causes pops 00150 * 00151 * Currently this method is used before init() once to save artist, title and album 00152 */ 00153 void EncoderVorbis::updateMetaData(char* artist, char* title, char* album) { 00154 m_metaDataTitle = title; 00155 m_metaDataArtist = artist; 00156 m_metaDataAlbum = album; 00157 } 00158 00159 void EncoderVorbis::initStream() { 00160 // set up analysis state and auxiliary encoding storage 00161 vorbis_analysis_init(&m_vdsp, &m_vinfo); 00162 vorbis_block_init(&m_vdsp, &m_vblock); 00163 00164 // set up packet-to-stream encoder; attach a random serial number 00165 srand(time(0)); 00166 ogg_stream_init(&m_oggs, getSerial()); 00167 00168 // add comment 00169 vorbis_comment_init(&m_vcomment); 00170 vorbis_comment_add_tag(&m_vcomment, "ENCODER", "mixxx/libvorbis"); 00171 if (m_metaDataArtist != NULL) 00172 vorbis_comment_add_tag(&m_vcomment, "ARTIST", m_metaDataArtist); 00173 if (m_metaDataTitle != NULL) 00174 vorbis_comment_add_tag(&m_vcomment, "TITLE", m_metaDataTitle); 00175 if (m_metaDataAlbum != NULL) 00176 vorbis_comment_add_tag(&m_vcomment, "ALBUM", m_metaDataAlbum); 00177 00178 // set up the vorbis headers 00179 ogg_packet headerInit; 00180 ogg_packet headerComment; 00181 ogg_packet headerCode; 00182 vorbis_analysis_headerout(&m_vdsp, &m_vcomment, &headerInit, &headerComment, &headerCode); 00183 ogg_stream_packetin(&m_oggs, &headerInit); 00184 ogg_stream_packetin(&m_oggs, &headerComment); 00185 ogg_stream_packetin(&m_oggs, &headerCode); 00186 00187 //The encoder is now inialized 00188 // Encode method will start streaming by sending the header first 00189 m_header_write = true; 00190 m_bStreamInitialized = true; 00191 } 00192 00193 int EncoderVorbis::initEncoder(int bitrate) { 00194 int ret; 00195 vorbis_info_init(&m_vinfo); 00196 00197 // initialize VBR quality based mode 00198 unsigned long samplerate = m_samplerate->get(); 00199 00200 ret = vorbis_encode_init(&m_vinfo, 2, samplerate, -1, bitrate*1000, -1); 00201 00202 if (ret == 0) { 00203 initStream(); 00204 } else { 00205 ret = -1; 00206 }; 00207 return ret; 00208 }