|
MythTV
0.26-pre
|
00001 // Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com> 00002 // 00003 // Use, modification and distribution is allowed without limitation, 00004 // warranty, or liability of any kind. 00005 // 00006 // modified 12-2004 by Kyle Schlansker to add 64 bit support 00007 // 00008 00009 #include "config.h" 00010 #include <stdint.h> 00011 #include <inttypes.h> 00012 00013 // C 00014 #include <cmath> 00015 #include <cstdlib> 00016 00017 // C++ 00018 #include <iostream> 00019 using namespace std; 00020 00021 // Qt 00022 #include <QCoreApplication> 00023 #include <QPainter> 00024 #include <QImage> 00025 00026 // MythTV 00027 #include <compat.h> 00028 #include <mythlogging.h> 00029 00030 // MythMusic 00031 #include "mainvisual.h" 00032 #include "synaesthesia.h" 00033 00034 Synaesthesia::Synaesthesia(void) : 00035 m_size(0,0), 00036 00037 m_maxStarRadius(1), 00038 m_fadeMode(Stars), 00039 m_pointsAreDiamonds(true), 00040 m_brightnessTwiddler(0.3), 00041 m_starSize(0.5), 00042 00043 m_outWidth(0), 00044 m_outHeight(0), 00045 00046 m_outputImage(NULL), 00047 00048 m_fgRedSlider(0.0), m_fgGreenSlider(0.5), 00049 m_bgRedSlider(0.75), m_bgGreenSlider(0.4), 00050 00051 m_energy_avg(80.0) 00052 { 00053 m_fps = 29; 00054 00055 coreInit(); // init cosTable, negSinTable, bitReverse 00056 setStarSize(m_starSize); // init scaleDown, maxStarRadius 00057 setupPalette(); // init palette 00058 } 00059 00060 Synaesthesia::~Synaesthesia() 00061 { 00062 if (m_outputImage) 00063 delete m_outputImage; 00064 } 00065 00066 void Synaesthesia::setupPalette(void) 00067 { 00068 #define sBOUND(x) ((x) > 255 ? 255 : (x)) 00069 #define sPEAKIFY(x) int(sBOUND((x) - (x)*(255-(x))/255/2)) 00070 #define sMAX(x,y) ((x) > (y) ? (x) : (y)) 00071 int i; 00072 double scale, fgRed, fgGreen, fgBlue, bgRed, bgGreen, bgBlue; 00073 fgRed = m_fgRedSlider; 00074 fgGreen = m_fgGreenSlider; 00075 fgBlue = 1.0 - sMAX(m_fgRedSlider,m_fgGreenSlider); 00076 //scale = sMAX(sMAX(fgRed,fgGreen),fgBlue); 00077 scale = (fgRed + fgGreen + fgBlue) / 2.0; 00078 fgRed /= scale; 00079 fgGreen /= scale; 00080 fgBlue /= scale; 00081 00082 bgRed = m_bgRedSlider; 00083 bgGreen = m_bgGreenSlider; 00084 bgBlue = 1.0 - sMAX(m_bgRedSlider,m_bgGreenSlider); 00085 //scale = sMAX(sMAX(bgRed,bgGreen),bgBlue); 00086 scale = (bgRed + bgGreen + bgBlue) / 2.0; 00087 bgRed /= scale; 00088 bgGreen /= scale; 00089 bgBlue /= scale; 00090 00091 for (i = 0; i < 256; i++) { 00092 int f = i & 15, b = i / 16; 00093 //palette[i * 3 + 0] = sPEAKIFY(b*bgRed*16+f*fgRed*16); 00094 //palette[i * 3 + 1] = sPEAKIFY(b*bgGreen*16+f*fgGreen*16); 00095 //palette[i * 3 + 2] = sPEAKIFY(b*bgBlue*16+f*fgBlue*16); 00096 00097 double red = b * bgRed * 16 + f * fgRed * 16; 00098 double green = b * bgGreen * 16 + f * fgGreen * 16; 00099 double blue = b * bgBlue * 16 + f * fgBlue * 16; 00100 00101 double excess = 0.0; 00102 for (int j = 0; j < 5; j++) 00103 { 00104 red += excess / 3; 00105 green += excess / 3; 00106 blue += excess / 3; 00107 excess = 0.0; 00108 00109 00110 if (red > 255) { excess += red - 255; red = 255; } 00111 if (green > 255) { excess += green - 255; green = 255; } 00112 if (blue > 255) { excess += blue - 255; blue = 255; } 00113 } 00114 00115 double scale = (0.5 + (red + green + blue) / 768.0) / 1.5; 00116 red *= scale; 00117 green *= scale; 00118 blue *= scale; 00119 00120 m_palette[i * 3 + 0] = sBOUND(int(red)); 00121 m_palette[i * 3 + 1] = sBOUND(int(green)); 00122 m_palette[i * 3 + 2] = sBOUND(int(blue)); 00123 } 00124 } 00125 00126 void Synaesthesia::resize(const QSize &newsize) 00127 { 00128 m_size = newsize; 00129 00130 m_size.setHeight(m_size.height() / 2); 00131 m_size.setWidth((m_size.width() / 4) * 4); 00132 m_outputBmp.size(m_size.width(), m_size.height()); 00133 m_lastOutputBmp.size(m_size.width(), m_size.height()); 00134 m_lastLastOutputBmp.size(m_size.width(), m_size.height()); 00135 m_outWidth = m_size.width(); 00136 m_outHeight = m_size.height(); 00137 00138 if (m_outputImage) 00139 delete m_outputImage; 00140 00141 m_size.setHeight(m_size.height() * 2); 00142 m_outputImage = new QImage(m_size, QImage::Format_Indexed8); 00143 00144 if (!m_outputImage) 00145 { 00146 LOG(VB_GENERAL, LOG_ERR, 00147 "outputImage in Synaesthesia::resize() is NULL"); 00148 return; 00149 } 00150 00151 for (int i = 0; i < 256; i++) 00152 m_outputImage->setColor(i, qRgba(m_palette[i * 3], m_palette[i * 3 + 1], 00153 m_palette[i * 3 + 2], 255)); 00154 00155 #if 0 00156 surface = SDL_SetVideoMode(size.width(), size.height(), 8, 0); 00157 00158 if (!surface) 00159 { 00160 LOG(VB_GENERAL, LOG_ERR, "Couldn't get SDL surface"); 00161 return; 00162 } 00163 00164 SDL_Color sdlPalette[256]; 00165 00166 for (int i = 0; i < 256; i++) 00167 { 00168 sdlPalette[i].r = m_palette[i * 3]; 00169 sdlPalette[i].g = m_palette[i * 3 + 1]; 00170 sdlPalette[i].b = m_palette[i * 3 + 2]; 00171 } 00172 00173 SDL_SetColors(surface, sdlPalette, 0, 256); 00174 #endif 00175 } 00176 00177 int Synaesthesia::bitReverser(int i) 00178 { 00179 int sum = 0; 00180 for (int j = 0; j < LogSize; j++) 00181 { 00182 sum = (i & 1) + sum * 2; 00183 i >>= 1; 00184 } 00185 00186 return sum; 00187 } 00188 00189 void Synaesthesia::fft(double *x, double *y) 00190 { 00191 int n2 = NumSamples, n1; 00192 for (int twoToTheK = 1; twoToTheK < NumSamples; twoToTheK *= 2) 00193 { 00194 n1 = n2; 00195 n2 /= 2; 00196 for (int j = 0; j < n2; j++) 00197 { 00198 double c = m_cosTable[j * twoToTheK & (NumSamples - 1)], 00199 s = m_negSinTable[j * twoToTheK & (NumSamples - 1)]; 00200 for (int i = j; i < NumSamples; i += n1) 00201 { 00202 int l = i + n2; 00203 double xt = x[i] - x[l]; 00204 x[i] = (x[i] + x[l]); 00205 double yt = y[i] - y[l]; 00206 y[i] = (y[i] + y[l]); 00207 x[l] = xt * c - yt * s; 00208 y[l] = xt * s + yt * c; 00209 } 00210 } 00211 } 00212 } 00213 00214 void Synaesthesia::setStarSize(double lsize) 00215 { 00216 double fadeModeFudge = (m_fadeMode == Wave ? 0.4 : 00217 (m_fadeMode == Flame ? 0.6 : 0.78)); 00218 00219 int factor; 00220 if (lsize > 0.0) 00221 factor = int(exp(log(fadeModeFudge) / (lsize * 8.0)) * 255); 00222 else 00223 factor = 0; 00224 00225 if (factor > 255) 00226 factor = 255; 00227 00228 for (int i = 0; i < 256; i++) 00229 m_scaleDown[i] = i * factor>>8; 00230 00231 m_maxStarRadius = 1; 00232 for (int i = 255; i; i = m_scaleDown[i]) 00233 m_maxStarRadius++; 00234 } 00235 00236 void Synaesthesia::coreInit(void) 00237 { 00238 for (int i = 0; i < NumSamples; i++) 00239 { 00240 m_negSinTable[i] = -sin(3.141592 * 2.0 / NumSamples * i); 00241 m_cosTable[i] = cos(3.141592 * 2.0 / NumSamples * i); 00242 m_bitReverse[i] = bitReverser(i); 00243 } 00244 } 00245 00246 #define output ((unsigned char*)m_outputBmp.data) 00247 #define lastOutput ((unsigned char*)m_lastOutputBmp.data) 00248 #define lastLastOutput ((unsigned char*)m_lastLastOutputBmp.data) 00249 00250 void Synaesthesia::addPixel(int x, int y, int br1, int br2) 00251 { 00252 if (x < 0 || x > m_outWidth || y < 0 || y >= m_outHeight) 00253 return; 00254 00255 unsigned char *p = output + x * 2 + y * m_outWidth * 2; 00256 if (p[0] + br1 < 255) 00257 p[0] += br1; 00258 else 00259 p[0] = 255; 00260 if (p[1] + br2 < 255) 00261 p[1] += br2; 00262 else 00263 p[1] = 255; 00264 } 00265 00266 void Synaesthesia::addPixelFast(unsigned char *p, int br1, int br2) 00267 { 00268 if (p[0] + br1 < 255) 00269 p[0] += br1; 00270 else 00271 p[0] = 255; 00272 if (p[1] + br2 < 255) 00273 p[1] += br2; 00274 else 00275 p[1] = 255; 00276 } 00277 00278 unsigned char Synaesthesia::getPixel(int x, int y, int where) 00279 { 00280 if (x < 0 || y < 0 || x >= m_outWidth || y >= m_outHeight) 00281 return 0; 00282 00283 return lastOutput[where]; 00284 } 00285 00286 void Synaesthesia::fadeFade(void) 00287 { 00288 register uint32_t *ptr = (uint32_t *)output; 00289 int i = m_outWidth * m_outHeight * 2 / sizeof(uint32_t); 00290 do { 00291 uint32_t x = *ptr; 00292 if (x) 00293 *(ptr++) = x - ((x & (uintptr_t)0xf0f0f0f0) >> 4) - 00294 ((x & (uintptr_t)0xe0e0e0e0) >> 5); 00295 else 00296 ptr++; 00297 } while (--i > 0); 00298 } 00299 00300 void Synaesthesia::fadePixelWave(int x, int y, int where, int step) 00301 { 00302 short j = short((int(getPixel(x - 1, y, where - 2)) + 00303 int(getPixel(x + 1, y, where + 2)) + 00304 int(getPixel(x, y - 1, where - step)) + 00305 int(getPixel(x, y + 1, where + step))) >> 2) + 00306 lastOutput[where]; 00307 00308 if (!j) 00309 { 00310 output[where] = 0; 00311 return; 00312 } 00313 j = j - lastLastOutput[where] - 1; 00314 if (j < 0) 00315 output[where] = 0; 00316 else if (j & (255 * 256)) 00317 output[where] = 255; 00318 else 00319 output[where] = j; 00320 } 00321 00322 void Synaesthesia::fadeWave(void) 00323 { 00324 unsigned short *t = m_lastLastOutputBmp.data; 00325 m_lastLastOutputBmp.data = m_lastOutputBmp.data; 00326 m_lastOutputBmp.data = m_outputBmp.data; 00327 m_outputBmp.data = t; 00328 00329 int x, y, i, j, start, end; 00330 int step = m_outWidth*2; 00331 for (x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2; 00332 x < m_outWidth; x++, i += 2, j += 2) 00333 { 00334 fadePixelWave(x, 0, i, step); 00335 fadePixelWave(x, 0, i + 1, step); 00336 fadePixelWave(x, m_outHeight - 1, j, step); 00337 fadePixelWave(x, m_outHeight - 1, j + 1, step); 00338 } 00339 00340 for (y = 1, i = m_outWidth * 2, j = m_outWidth * 4 - 2; 00341 y < m_outHeight; y++, i += step, j += step) 00342 { 00343 fadePixelWave(0, y, i, step); 00344 fadePixelWave(0, y, i + 1, step); 00345 fadePixelWave(m_outWidth - 1, y, j, step); 00346 fadePixelWave(m_outWidth - 1, y, j + 1, step); 00347 } 00348 00349 for (y = 1, start = m_outWidth * 2 + 2, end = m_outWidth * 4 - 2; 00350 y < m_outHeight - 1; y++, start += step, end += step) 00351 { 00352 int i = start; 00353 do 00354 { 00355 short j = short((int(lastOutput[i - 2]) + 00356 int(lastOutput[i + 2]) + 00357 int(lastOutput[i - step]) + 00358 int(lastOutput[i + step])) >> 2) + 00359 lastOutput[i]; 00360 if (!j) 00361 { 00362 output[i] = 0; 00363 } 00364 else 00365 { 00366 j = j - lastLastOutput[i] - 1; 00367 if (j < 0) 00368 output[i] = 0; 00369 else if (j & (255*256)) 00370 output[i] = 255; 00371 else 00372 output[i] = j; 00373 } 00374 } while(++i < end); 00375 } 00376 } 00377 00378 void Synaesthesia::fadePixelHeat(int x, int y, int where, int step) 00379 { 00380 short j = short((int(getPixel(x - 1, y, where - 2)) + 00381 int(getPixel(x + 1, y, where + 2)) + 00382 int(getPixel(x, y - 1, where - step)) + 00383 int(getPixel(x, y + 1, where + step))) >> 2) + 00384 lastOutput[where]; 00385 if (!j) 00386 { 00387 output[where] = 0; 00388 return; 00389 } 00390 j = j -lastLastOutput[where] - 1; 00391 if (j < 0) 00392 output[where] = 0; 00393 else if (j & (255 * 256)) 00394 output[where] = 255; 00395 else 00396 output[where] = j; 00397 } 00398 00399 void Synaesthesia::fadeHeat(void) 00400 { 00401 unsigned short *t = m_lastLastOutputBmp.data; 00402 m_lastLastOutputBmp.data = m_lastOutputBmp.data; 00403 m_lastOutputBmp.data = m_outputBmp.data; 00404 m_outputBmp.data = t; 00405 00406 int x, y, i, j, start, end; 00407 int step = m_outWidth * 2; 00408 for (x = 0, i = 0, j = m_outWidth * (m_outHeight - 1) * 2; 00409 x < m_outWidth; x++, i += 2, j += 2) 00410 { 00411 fadePixelHeat(x, 0, i, step); 00412 fadePixelHeat(x, 0, i + 1, step); 00413 fadePixelHeat(x, m_outHeight - 1, j, step); 00414 fadePixelHeat(x, m_outHeight - 1, j + 1, step); 00415 } 00416 00417 for(y = 1, i = m_outWidth * 2, j = m_outWidth * 4 - 2; y < m_outHeight; 00418 y++, i += step, j += step) 00419 { 00420 fadePixelHeat(0, y, i, step); 00421 fadePixelHeat(0, y, i + 1, step); 00422 fadePixelHeat(m_outWidth - 1, y, j, step); 00423 fadePixelHeat(m_outWidth - 1, y, j + 1, step); 00424 } 00425 00426 for(y = 1, start = m_outWidth * 2 + 2, end = m_outWidth * 4 - 2; 00427 y < m_outHeight - 1; y++, start += step, end += step) 00428 { 00429 int i = start; 00430 do 00431 { 00432 short j = short((int(lastOutput[i - 2]) + 00433 int(lastOutput[i + 2]) + 00434 int(lastOutput[i - step]) + 00435 int(lastOutput[i + step])) >> 2) + 00436 lastOutput[i]; 00437 if (!j) 00438 output[i] = 0; 00439 else 00440 { 00441 j = j - lastLastOutput[i] + 00442 ((lastLastOutput[i] - lastOutput[i]) >> 2) - 1; 00443 if (j < 0) 00444 output[i] = 0; 00445 else if (j & (255*256)) 00446 output[i] = 255; 00447 else 00448 output[i] = j; 00449 } 00450 } while(++i < end); 00451 } 00452 } 00453 00454 void Synaesthesia::fade(void) 00455 { 00456 switch(m_fadeMode) 00457 { 00458 case Stars: fadeFade(); break; 00459 case Flame: fadeHeat(); break; 00460 case Wave: fadeWave(); break; 00461 default: break; 00462 } 00463 } 00464 00465 bool Synaesthesia::process(VisualNode *node) 00466 { 00467 fade(); 00468 00469 if (!node) 00470 return false; 00471 00472 double x[NumSamples], y[NumSamples]; 00473 double a[NumSamples], b[NumSamples]; 00474 double energy; 00475 int clarity[NumSamples]; 00476 int i, j, k; 00477 00478 int brightFactor = int(Brightness * m_brightnessTwiddler / (m_starSize + 0.01)); 00479 00480 int numSamps = NumSamples; 00481 if (node->length < NumSamples) 00482 numSamps = node->length; 00483 00484 memset(x, 0, sizeof(x)); 00485 memset(y, 0, sizeof(y)); 00486 00487 for (i = 0; i < numSamps; i++) 00488 { 00489 x[i] = node->left[i]; 00490 if (node->right) 00491 y[i] = node->right[i]; 00492 else 00493 y[i] = x[i]; 00494 } 00495 00496 fft(x, y); 00497 00498 energy = 0.0; 00499 00500 for (i = 0 + 1; i < NumSamples / 2; i++) 00501 { 00502 double x1 = x[m_bitReverse[i]], 00503 y1 = y[m_bitReverse[i]], 00504 x2 = x[m_bitReverse[NumSamples - i]], 00505 y2 = y[m_bitReverse[NumSamples - i]], 00506 aa, bb; 00507 a[i] = sqrt(aa = (x1 + x2) * (x1 + x2) + (y1 - y2) * (y1 - y2)); 00508 b[i] = sqrt(bb = (x1 - x2) * (x1 - x2) + (y2 + y2) * (y1 + y2)); 00509 if (aa + bb != 0.0) 00510 clarity[i] = (int)(((x1 + x2) * (x1 - x2) + (y1 + y2) * (y1 - y2)) / 00511 (aa + bb) * 256); 00512 else 00513 clarity[i] = 0; 00514 00515 energy += (aa + bb) * i * i; 00516 } 00517 00518 energy = sqrt(energy / NumSamples) / 65536.0; 00519 00520 //int heightFactor = NumSamples / 2 / outHeight + 1; 00521 //int actualHeight = NumSamples / 2 / heightFactor; 00522 //int heightAdd = (outHeight + actualHeight) >> 1; 00523 00524 double brightFactor2 = (brightFactor / 65536.0 / NumSamples) * 00525 sqrt(m_outHeight * m_outWidth / (320.0 * 200.0)); 00526 00527 m_energy_avg = m_energy_avg * 0.95 + energy * 0.05; 00528 if (m_energy_avg > 0.0) 00529 brightFactor2 *= 80.0 / (m_energy_avg + 5.0); 00530 00531 for (i = 1; i < NumSamples / 2; i++) 00532 { 00533 if (a[i] > 0 || b[i] > 0) 00534 { 00535 int h = (int)(b[i] * m_outWidth / (a[i] + b[i])); 00536 int br1, br2, br = (int)((a[i] + b[i]) * i * brightFactor2); 00537 br1 = br * (clarity[i] + 128) >> 8; 00538 br2 = br * (128 - clarity[i]) >> 8; 00539 if (br1 < 0) br1 = 0; else if (br1 > 255) br1 = 255; 00540 if (br2 < 0) br2 = 0; else if (br2 > 255) br2 = 255; 00541 00542 int px = h, 00543 py = m_outHeight - i * m_outHeight / (NumSamples / 2); 00544 00545 if (m_pointsAreDiamonds) 00546 { 00547 addPixel(px, py, br1, br2); 00548 br1 = m_scaleDown[br1]; 00549 br2 = m_scaleDown[br2]; 00550 00551 for(j = 1; br1 > 0 || br2 > 0; 00552 j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2]) 00553 { 00554 for (k = 0; k < j; k++) 00555 { 00556 addPixel(px - j + k,py - k, br1, br2); 00557 addPixel(px + k, py - j + k, br1, br2); 00558 addPixel(px + j - k, py + k, br1, br2); 00559 addPixel(px - k, py + j - k, br1, br2); 00560 } 00561 } 00562 } 00563 else 00564 { 00565 if (px < m_maxStarRadius || py < m_maxStarRadius || 00566 px > m_outWidth - m_maxStarRadius || 00567 py > m_outHeight - m_maxStarRadius) 00568 { 00569 addPixel(px, py, br1, br2); 00570 for( j = 1; br1 > 0 || br2 > 0; 00571 j++, br1 = m_scaleDown[br1], br2 = m_scaleDown[br2]) 00572 { 00573 addPixel(px + j, py, br1, br2); 00574 addPixel(px, py + j, br1, br2); 00575 addPixel(px - j, py, br1, br2); 00576 addPixel(px, py - j, br1, br2); 00577 } 00578 } 00579 else 00580 { 00581 unsigned char *p = output + px * 2 + py * m_outWidth * 2, 00582 *p1 = p, *p2 = p, *p3 = p, *p4 = p; 00583 addPixelFast(p, br1, br2); 00584 for (; br1 > 0 || br2 > 0; 00585 br1 = m_scaleDown[br1], br2 = m_scaleDown[br2]) 00586 { 00587 p1 += 2; 00588 addPixelFast(p1, br1, br2); 00589 p2 -= 2; 00590 addPixelFast(p2, br1, br2); 00591 p3 += m_outWidth * 2; 00592 addPixelFast(p3, br1, br2); 00593 p4 -= m_outWidth*2; 00594 addPixelFast(p4, br1, br2); 00595 } 00596 } 00597 } 00598 } 00599 } 00600 00601 return false; 00602 } 00603 00604 bool Synaesthesia::draw(QPainter *p, const QColor &back) 00605 { 00606 if (!m_outputImage) 00607 return true; 00608 00609 (void)back; 00610 00611 register uint32_t *ptrOutput = (uint32_t *)output; 00612 00613 for (int j = 0; j < m_outHeight * 2; j += 2) 00614 { 00615 uint32_t *ptrTop = (uint32_t *)(m_outputImage->scanLine(j)); 00616 uint32_t *ptrBot = (uint32_t *)(m_outputImage->scanLine(j+1)); 00617 00618 int i = m_outWidth / 4; 00619 00620 do 00621 { 00622 register unsigned int const r1 = *(ptrOutput++); 00623 register unsigned int const r2 = *(ptrOutput++); 00624 00625 register unsigned int const v = ((r1 & 0x000000f0ul) >> 4) | 00626 ((r1 & 0x0000f000ul) >> 8) | 00627 ((r1 & 0x00f00000ul) >> 12) | 00628 ((r1 & 0xf0000000ul) >> 16); 00629 00630 *(ptrTop++) = v | (((r2 & 0x000000f0ul) << 12) | 00631 ((r2 & 0x0000f000ul) << 8) | 00632 ((r2 & 0x00f00000ul) << 4) | 00633 ((r2 & 0xf0000000ul))); 00634 00635 *(ptrBot++) = v | (((r2 & 0x000000f0ul) << 12) | 00636 ((r2 & 0x0000f000ul) << 8) | 00637 ((r2 & 0x00f00000ul) << 4) | 00638 ((r2 & 0xf0000000ul))); 00639 } while (--i); 00640 } 00641 00642 p->drawImage(0, 0, *m_outputImage); 00643 00644 return true; 00645 } 00646 00647 static class SynaesthesiaFactory : public VisFactory 00648 { 00649 public: 00650 const QString &name(void) const 00651 { 00652 static QString name = QCoreApplication::translate("Visualizers", 00653 "Synaesthesia"); 00654 return name; 00655 } 00656 00657 uint plugins(QStringList *list) const 00658 { 00659 *list << name(); 00660 return 1; 00661 } 00662 00663 VisualBase *create(MainVisual *parent, const QString &pluginName) const 00664 { 00665 (void)parent; 00666 (void)pluginName; 00667 return new Synaesthesia(); 00668 } 00669 } SynaesthesiaFactory;
1.7.6.1