static BOOL ML_LoadPatterns(void)
/*
	Loads all patterns of a modfile and converts them into the
	3 byte format.
*/
{
    int t,s,tracks=0;

    if(!AllocPatterns()) return 0;
    if(!AllocTracks()) return 0;

    /* Allocate temporary buffer for loading
       and converting the patterns */

    if(!(patbuf=(MODNOTE *)MyCalloc(64U*of.numchn,sizeof(MODNOTE)))) return 0;

    for(t=0;t<of.numpat;t++){

	/* Load the pattern into the temp buffer
	   and convert it */

	for(s=0;s<(64L * of.numchn);s++){
	    patbuf[s].a=_mm_read_UBYTE();
	    patbuf[s].b=_mm_read_UBYTE();
	    patbuf[s].c=_mm_read_UBYTE();
	    patbuf[s].d=_mm_read_UBYTE();
	}

	for(s=0;s<of.numchn;s++){
	    if(!(of.tracks[tracks++]=ConvertTrack(patbuf+s))) return 0;
	}
    }

    return 1;
}
Example #2
0
/* Loads all patterns of a modfile and converts them into the 3 byte format. */
static BOOL ML_LoadPatterns(void)
{
	int t, s, tracks = 0;

	if (!AllocPatterns()) {
		return 0;
	}
	if (!AllocTracks()) {
		return 0;
	}

	/* Allocate temporary buffer for loading and converting the patterns */
	if (!(patbuf = (MODNOTE *)MikMod_calloc(64U * of.numchn, sizeof(MODNOTE))))
		return 0;


	/* patterns start here */
	_mm_fseek(modreader, 0xA66, SEEK_SET);
	for (t = 0; t < of.numpat; t++) {
		/* Load the pattern into the temp buffer and convert it */
		for (s = 0; s < (64U * of.numchn); s++) {
			patbuf[s].a = _mm_read_UBYTE(modreader);
			patbuf[s].b = _mm_read_UBYTE(modreader);
			patbuf[s].c = _mm_read_UBYTE(modreader);
			patbuf[s].d = _mm_read_UBYTE(modreader);
		}
		for (s = 0; s < of.numchn; s++) {
			if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s))) {
				return 0;
			}
		}
	}
	return 1;
}
void SpotifyClient::SendSearchResponse(sp_search* result) {
  // Take the request out of the queue
  spotify_pb::SearchRequest req = pending_searches_.take(result);

  // Prepare the response
  spotify_pb::SpotifyMessage message;
  spotify_pb::SearchResponse* response = message.mutable_search_response();

  *response->mutable_request() = req;

  // Check for errors
  sp_error error = sp_search_error(result);
  if (error != SP_ERROR_OK) {
    response->set_error(sp_error_message(error));

    handler_->SendMessage(message);
    sp_search_release(result);
    return;
  }

  // Get the list of tracks from the search
  int count = sp_search_num_tracks(result);
  for (int i=0 ; i<count ; ++i) {
    sp_track* track = sp_search_track(result, i);
    ConvertTrack(track, response->add_result());
  }

  // Get the albums from the search.  All these should be resolved by now.
  QList<sp_albumbrowse*> browses = pending_search_album_browses_.take(result);
  foreach (sp_albumbrowse* browse, browses) {
    sp_album* album = sp_albumbrowse_album(browse);
    spotify_pb::Album* msg = response->add_album();

    ConvertAlbum(album, msg->mutable_metadata());
    ConvertAlbumBrowse(browse, msg->mutable_metadata());

    // Add all tracks
    const int tracks = sp_albumbrowse_num_tracks(browse);
    for (int i=0 ; i<tracks ; ++i) {
      ConvertTrack(sp_albumbrowse_track(browse, i), msg->add_track());
    }

    sp_albumbrowse_release(browse);
  }
Example #4
0
ap_result MikSTX::ModuleConverter(APAgent_ConvertModule *convInfo)
{
	int32 t, u, track = 0;
	int32 version = 0;
	SAMPLE *q;
	PFile *file = convInfo->moduleFile;
	ap_result retVal = AP_OK;

	try
	{
		// Clear the buffer pointers
		mh        = NULL;
		stxBuf    = NULL;
		posLookup = NULL;
		paraPtr   = NULL;

		// Allocate buffers
		if ((mh = new STXHEADER) == NULL)
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		if ((stxBuf = new STXNOTE[4 * 64]) == NULL)
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		if ((posLookup = new uint8[256]) == NULL)
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		// Clear the buffers
		memset(mh, 0, sizeof(STXHEADER));
		memset(stxBuf, 0, sizeof(STXNOTE) * 4 * 64);
		memset(posLookup, -1, 256);

		// Try to read module header
		file->ReadString(mh->songName, 20);
		file->ReadString(mh->trackerName, 8);

		mh->patSize    = file->Read_L_UINT16();
		mh->unknown1   = file->Read_L_UINT16();
		mh->patPtr     = file->Read_L_UINT16();
		mh->insPtr     = file->Read_L_UINT16();
		mh->chnPtr     = file->Read_L_UINT16();
		mh->unknown2   = file->Read_L_UINT16();
		mh->unknown3   = file->Read_L_UINT16();
		mh->masterMult = file->Read_UINT8();
		mh->initSpeed  = file->Read_UINT8() >> 4;
		mh->unknown4   = file->Read_L_UINT16();
		mh->unknown5   = file->Read_L_UINT16();
		mh->patNum     = file->Read_L_UINT16();
		mh->insNum     = file->Read_L_UINT16();
		mh->ordNum     = file->Read_L_UINT16();
		mh->unknown6   = file->Read_L_UINT16();
		mh->unknown7   = file->Read_L_UINT16();
		mh->unknown8   = file->Read_L_UINT16();
		file->Read(mh->scrm, 4);

		if (file->IsEOF())
		{
			ShowError(IDS_MIKC_ERR_LOADING_HEADER);
			throw PUserException();
		}

		// Set module variables
		of.songName.SetString(mh->songName, &charSet850);
		of.numPat    = mh->patNum;
		of.repPos    = 0;
		of.numIns    = of.numSmp = mh->insNum;
		of.initSpeed = mh->initSpeed;
		of.initTempo = 125;
		of.numChn    = 4;
		of.flags    |= UF_S3MSLIDES;
		of.bpmLimit  = 32;

		if ((paraPtr = new uint16[of.numIns + of.numPat]) == NULL)
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		// Read the instrument + patterns parapointers
		file->Seek(mh->insPtr << 4, PFile::pSeekBegin);
		file->ReadArray_L_UINT16s(paraPtr, of.numIns);

		file->Seek(mh->patPtr << 4, PFile::pSeekBegin);
		file->ReadArray_L_UINT16s(paraPtr + of.numIns, of.numPat);

		// Check module version
		file->Seek(paraPtr[of.numIns] << 4, PFile::pSeekBegin);
		version = file->Read_L_UINT16();
		if (version == mh->patSize)
		{
			version = 0x10;
			convInfo->modKind.LoadString(res, IDS_MIKC_NAME_STX_10);
		}
		else
		{
			version = 0x11;
			convInfo->modKind.LoadString(res, IDS_MIKC_NAME_STX_11);
		}

		convInfo->fileType.LoadString(res, IDS_MIKC_MIME_STX);

		// Read the order data
		file->Seek((mh->chnPtr << 4) + 32, PFile::pSeekBegin);

		if (!(AllocPositions(mh->ordNum)))
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		for (t = 0; t < mh->ordNum; t++)
		{
			of.positions[t] = file->Read_UINT8();
			file->Seek(4, PFile::pSeekCurrent);
		}

		of.numPos    = 0;
		posLookupCnt = mh->ordNum;

		for (t = 0; t < mh->ordNum; t++)
		{
			int32 order = of.positions[t];
			if (order == 255)
				order = LAST_PATTERN;

			of.positions[of.numPos] = order;
			posLookup[t] = of.numPos;		// Bug fix for freaky S3Ms

			if (of.positions[t] < 254)
				of.numPos++;
			else
			{
				// Special end of song pattern
				if ((order == LAST_PATTERN) && (!curious))
					break;
			}
		}

		if (file->IsEOF())
		{
			ShowError(IDS_MIKC_ERR_LOADING_HEADER);
			throw PUserException();
		}

		// Load samples
		if (!(AllocSamples()))
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		for (q = of.samples, t = 0; t < of.numIns; t++, q++)
		{
			STXSAMPLE s;

			// Seek to instrument position
			file->Seek(((int32)paraPtr[t]) << 4, PFile::pSeekBegin);

			// And load sample info
			s.type = file->Read_UINT8();

			file->ReadString(s.fileName, 12);

			s.memSegH = file->Read_UINT8();
			s.memSegL = file->Read_L_UINT16();
			s.length  = file->Read_L_UINT32();
			s.loopBeg = file->Read_L_UINT32();
			s.loopEnd = file->Read_L_UINT32();
			s.volume  = file->Read_UINT8();
			s.dsk     = file->Read_UINT8();
			s.pack    = file->Read_UINT8();
			s.flags   = file->Read_UINT8();
			s.c2Spd   = file->Read_L_UINT32();

			file->Read(s.unused, 12);
			file->ReadString(s.sampName, 28);
			file->Read(s.scrs, 4);

			if (file->IsEOF())
			{
				ShowError(IDS_MIKC_ERR_LOADING_SAMPLEINFO);
				throw PUserException();
			}

			q->sampleName.SetString(s.sampName, &charSet850);
			q->speed      = (s.c2Spd * 8363) / 8448;
			q->length     = s.length;
			q->loopStart  = s.loopBeg;
			q->loopEnd    = s.loopEnd;
			q->volume     = s.volume;
			q->seekPos    = (((int32)s.memSegH) << 16 | s.memSegL) << 4;
			q->flags     |= SF_SIGNED;

			if (s.flags & 1)
				q->flags |= SF_LOOP;

			if (s.flags & 4)
				q->flags |= SF_16BITS;
		}

		// Load pattern info
		of.numTrk = of.numPat * of.numChn;

		if (!(AllocTracks()))
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		if (!(AllocPatterns()))
		{
			ShowError(IDS_MIKC_ERR_MEMORY);
			throw PUserException();
		}

		for (t = 0; t < of.numPat; t++)
		{
			// Seek to pattern position (+2 skip pattern length)
			file->Seek((((int32)paraPtr[of.numIns + t]) << 4) + (version == 0x10 ? 2 : 0), PFile::pSeekBegin);
			ReadPattern(file);

			for (u = 0; u < of.numChn; u++)
			{
				if (!(of.tracks[track++] = ConvertTrack(&stxBuf[u * 64])))
				{
					ShowError(IDS_MIKC_ERR_INITIALIZE);
					throw PUserException();
				}
			}
		}
	}
	catch(PUserException e)
	{
		retVal = AP_ERROR;
	}

	// Clean up again
	delete[] stxBuf;
	stxBuf = NULL;

	delete[] paraPtr;
	paraPtr = NULL;

	delete[] posLookup;
	posLookup = NULL;

	delete mh;
	mh = NULL;

	return (retVal);
}
Example #5
0
int32 RipView::RipThread(void *data)
{
	acquire_sem(abort_thread);
	
	RipView *view = (RipView*)data;
	
	view->Window()->Lock();
	view->fProgressBar->SetText(_T("Creating songs from CD..."));
	view->Window()->Unlock();
	
	// Get all the preferences that we'll need in one shot to save on piddling
	// around with locking
	prefsLock.Lock();
	
	int16 foldermode;
	if (preferences.FindInt16("foldermode",&foldermode)!=B_OK)
		foldermode=1;
	
	int16 trackname;
	if (preferences.FindInt16("namestyle",&trackname)!=B_OK)
		trackname=0;
	
	int16 bitrate;
	if (preferences.FindInt16("bitrate",&bitrate)!=B_OK)
		bitrate=1;
	
	BString destfolder;
	if (preferences.FindString("path",&destfolder)!=B_OK)
		destfolder="/boot/home/music";
	
	bool use_mp3;
	if (preferences.FindBool("usemp3",&use_mp3)!=B_OK)
		use_mp3=false;
	
	if (use_mp3 && (!gMP3Format || !gMP3Codec))
		use_mp3=false;
	
	bool make_playlist;
	if (preferences.FindBool("makeplaylist",&make_playlist)!=B_OK)
		make_playlist=true;
	
	BString playlistfolder;
	if (preferences.FindString("playlistfolder",&playlistfolder)!=B_OK)
	{
		playlistfolder=destfolder;
		playlistfolder << "/playlists";
	}
	
	prefsLock.Unlock();
	

	bool write_attributes=true;
	
	dev_t destDevice = dev_for_path(destfolder.String());
	BVolume volume(destDevice);
	if (!volume.KnowsAttr())
	{
		write_attributes=false;
		//printf("Volume for %s doesn't support attributes\n",destfolder.String());
	}
	
	// If we are grouping by artist or artist/album, we need to make sure the
	// directory exists
	switch(foldermode)
	{
		case 1:	// Artist
		{
			destfolder << "/" << gCDData.Artist() << "/";
			break;
		}
		case 2: // Album
		{
			destfolder << "/" << gCDData.Album() << "/";
			break;
		}
		case 3: // Artist & Album
		{
			destfolder << "/" << gCDData.Artist() << "/" << gCDData.Album() << "/";
			break;
		}
		default: // no special grouping
		{
			break;
		}
	}
	if (create_directory(destfolder.String(),0777)!=B_OK)
	{
		BEntry dir(destfolder.String());
		if (!dir.Exists())
		{
			BString errormsg;
			#ifdef SYS_ZETA
				errormsg=_T("Uh-oh... SimplyVorbis couldn't create the folder '");
				errormsg << destfolder << _T("RipViewMultiline1");
				
				BAlert *alert = new BAlert("SimplyVorbis",errormsg.String(),_T("OK"));
			#else
				errormsg="Uh-oh... SimplyVorbis couldn't create the folder '";
				errormsg << destfolder << "'.\n\nThis may have happened for a number of different reasons, but "
					"most often happens when making music files on a non-BeOS drive, such as one shared "
					"with Windows. Certain characters, such as question marks and slashes cause problems "
					"on these disks. You may want to check the names of the artist, album, and songs for "
					"such characters and put a good substitute in its place or remove the character entirely.";
				
				BAlert *alert = new BAlert("SimplyVorbis",errormsg.String(),"OK");
			#endif
			alert->Go();
			
			view->fRipThread = -1;
			view->Window()->PostMessage(M_STOP_ENCODING);
			
			release_sem(abort_thread);
			return 0;
		}
	}
	
	// make the directory only if the user wants to create playlists
	if (make_playlist && create_directory(playlistfolder.String(),0777)!=B_OK)
	{
		BEntry playdir(playlistfolder.String());
		if (!playdir.Exists())
		{
			BString errormsg;
			#ifdef SYS_ZETA			
			errormsg=_T("Uh-oh... SimplyVorbis couldn't create the folder '");
			errormsg << playlistfolder << _T("RIpViewMultiline2");
			
			BAlert *alert = new BAlert("SimplyVorbis",errormsg.String(),_T("OK"));
			#else
			errormsg="Uh-oh... SimplyVorbis couldn't create the folder '";
			errormsg << playlistfolder << "' for the playlists.\n\nThis may have happened for a number of different reasons, but "
				"most often happens when making playlists on a non-BeOS drive, such as one shared "
				"with Windows. Certain characters, such as question marks and slashes cause problems "
				"on these disks. You may want to check the names of the artist, album, and songs for "
				"such characters and put a good substitute in its place or remove the character entirely."
				"For the moment, your music will be created, but the playlist will not. Sorry.";
			
			BAlert *alert = new BAlert("SimplyVorbis",errormsg.String(),"OK");
			#endif
			alert->Go();
			make_playlist=false;
		}
	}
	
	// *Sigh* FAT32 volumes don't support use of question marks. I wonder what else... :(
	if (!write_attributes)
	{
		destfolder.RemoveAll("?");
		playlistfolder.RemoveAll("?");
	}
	
	BString trackPrefix;
	switch(trackname)
	{
		case 0:	// Artist
		{
			trackPrefix = gCDData.Artist();
			trackPrefix+=" - ";
			break;
		}
		case 1: // Album
		{
			trackPrefix = gCDData.Album();
			trackPrefix+=" - ";
			break;
		}
		case 2: // Artist & Album
		{
			trackPrefix = gCDData.Artist();
			trackPrefix << " - " << gCDData.Album() << " - ";
			break;
		}
		default: // no special grouping
		{
			break;
		}
	}
	
	// Populate the list of tracks to be ripped
	view->Window()->Lock();
	
	for(int32 i=view->fRipList->CountItems(); i>0; i--)
	{
		BStringItem *item = (BStringItem *)view->fRipList->RemoveItem(i);
		delete item;
	}
	
	for(int32 i=0; i<gTrackList.CountItems(); i++)
	{
		if (*(gTrackList.ItemAt(i)))
		{
			if (gCDDrive.IsDataTrack(i))
			{
				*(gTrackList.ItemAt(i)) = false;
				continue;
			}
			
			view->fRipList->AddItem(new BStringItem(gCDData.TrackAt(i)));
		}
	}
	view->Window()->Unlock();
	
	// playlists are a nice thing for quite a few people, apparently. :)
	BFile playlistfile;
	BString playlistfilename=playlistfolder;
	
	if (make_playlist)
	{
		playlistfilename << "/" << gCDData.Artist() << " - " << gCDData.Album() << ".m3u";
		
		// Append to playlists instead of overwriting them
		BEntry playlistentry(playlistfilename.String());
		if (playlistentry.Exists())
		{
			if (playlistfile.SetTo(playlistfilename.String(),B_READ_WRITE)==B_OK)
			{
				// HACK: This works around a bug in R5's BFile::Seek implementation
//				playlistfile.Seek(SEEK_END,0);
				off_t filesize;
				playlistfile.GetSize(&filesize);
				if (filesize>0)
				{
					char data[filesize];
					playlistfile.Read(&data,filesize);
				}
			}
			else
				playlistfile.SetTo(playlistfilename.String(),B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
		}
		else
			playlistfile.SetTo(playlistfilename.String(),B_READ_WRITE | B_CREATE_FILE);
		
		if (playlistfile.InitCheck()!=B_OK)
		{
			BString errormsg;
			#ifdef SYS_ZETA
			errormsg=_T("Uh-oh... SimplyVorbis couldn't create the playlist '");
			errormsg << playlistfilename << _T("RIpViewMultiline3");
			BAlert *alert = new BAlert("SimplyVorbis",errormsg.String(),_T("OK"));
			#else
			errormsg="Uh-oh... SimplyVorbis couldn't create the playlist '";
			errormsg << playlistfilename << "'.\n\nThis may have happened for a number of different reasons, but "
				"most often happens when making playlists on a non-BeOS drive, such as one shared "
				"with Windows. Certain characters, such as question marks and slashes cause problems "
				"on these disks. You may want to check the names of the artist, album, and songs for "
				"such characters and put a good substitute in its place or remove the character entirely."
				"For the moment, your music will be created, but the playlist will not. Sorry.";
			BAlert *alert = new BAlert("SimplyVorbis",errormsg.String(),"OK");
			#endif	

			alert->Go();
			make_playlist = false;
		}
	}
	
	BString syscmd;
	BMessenger msgr(view);
	bool copyfailed = false;
	bool showfailalert = 0;
	for (int32 i = 0; i < gTrackList.CountItems(); i++)
	{
		if (*(gTrackList.ItemAt(i)))
		{
			view->Window()->Lock();
			BStringItem *oldtrack = (BStringItem*)view->fRipList->RemoveItem(0L);
			view->Window()->Unlock();
			delete oldtrack;
			
			BString filename(trackPrefix);
			filename += gCDData.TrackAt(i);
			
			// FAT32 is such a pain in the neck here. Certain characters which are *just* *fine* for
			// other OSes are illegal. Just for the user's sake, we will constrain ourselves to its limits.
			// This means that question marks, double quotes, and colons will be substituted or removed.
			// Although it has not popped up in testing, backslashes are also substituted just in case
			filename.RemoveSet("?");
			filename.ReplaceAll("/", "-");
			filename.ReplaceAll("\\", "-");
			filename.ReplaceAll("\"", "'");
			filename.ReplaceAll(":", "-");
			
			// Set things up for the progress bar for ripping this particular track
			view->Window()->Lock();
			
			view->fProgressLabel=_T("Converting '");
			view->fProgressLabel << gCDData.TrackAt(i) << "'";
			view->fProgressBar->SetText(view->fProgressLabel.String());
			
			rgb_color blue={50,150,255,255};
			view->fProgressBar->SetBarColor(blue);
			
			cdaudio_time tracklength;
			gCDDrive.GetTimeForTrack(i+1,tracklength);
			view->fProgressBar->SetMaxValue( (tracklength.minutes * 60) + tracklength.seconds);
			view->fProgressDelta = 100.0 / float( (tracklength.minutes * 60) + tracklength.seconds);
			view->fProgressBar->Reset();
			
			view->Window()->Unlock();
			
			BString ripname(filename);
			ripname += use_mp3 ? ".mp3" : ".ogg";
			cdaudio_time rippedtime;
			
			status_t ripstat=B_OK;
			
			if (use_mp3)
				ripstat=ConvertTrack(gCDDrive.GetDrivePath(),ripname.String(),i+1,*gMP3Format,
									*gMP3Codec,&msgr,abort_thread);
			else
				ripstat=VorbifyTrack(gCDDrive.GetDrivePath(),ripname.String(),i+1,&msgr,abort_thread);
			
			if (ripstat==B_INTERRUPTED)
			{
				//  This will unblock the window
				view->fRipThread = -1;
				view->Window()->PostMessage(M_STOP_ENCODING);
				release_sem(abort_thread);
				BEntry entry(ripname.String());
				if (entry.Exists())
					entry.Remove();
				return -1;
			}
			else
			if (ripstat!=B_OK)
			{
				// Because things aren't really accurate on some CDs on the last audio track, 
				// we bear with it.
				if (!gCDDrive.IsDataTrack(i+1))
				{
					view->Window()->Lock();
					view->fProgressBar->SetText(_T("Couldn't read song from the CD. Sorry!"));
					view->Window()->Unlock();
					BEntry entry(ripname.String());
					if (entry.Exists())
						entry.Remove();
					continue;
				}
				else
				{
					view->Window()->Lock();
					rgb_color darkblue={0,0,127,255};
					view->fProgressBar->Update(view->fProgressBar->MaxValue() - 
												view->fProgressBar->CurrentValue(),
												_T("Finishing early. This is not a bad thing."));
					view->fProgressBar->SetBarColor(darkblue);
					view->Window()->Unlock();
					rippedtime = GetTimeRipped();
				}
			}
			else
			{
				view->Window()->Lock();
				rgb_color darkblue={0,0,127,255};
				view->fProgressBar->SetBarColor(darkblue);
				view->Window()->Unlock();
			}
			
			// This will ensure that the drive isn't running for an unnecesary amount of time
			gCDDrive.Stop();
			
			// Set the mime type
			filename=destfolder;
			filename << trackPrefix << gCDData.TrackAt(i) << (use_mp3 ? ".mp3" : ".ogg");
			
			BNode node(ripname.String());
#ifndef FAKE_RIPPING
			if (node.InitCheck()==B_OK)
#endif
			{
				BNodeInfo nodeinfo(&node);
				if (nodeinfo.InitCheck()==B_OK)
					nodeinfo.SetType( use_mp3 ? "audio/x-mpeg" : "audio/x-vorbis");
				
				if (write_attributes)
				{
					if (strlen(gCDData.Genre())>0)
						node.WriteAttr("Audio:Genre",B_STRING_TYPE,0,gCDData.Genre(),strlen(gCDData.Genre())+1);
					node.WriteAttr("Audio:Comment",B_STRING_TYPE,0,"Created by SimplyVorbis",
									strlen("Created by SimplyVorbis")+1);
					node.WriteAttr("Audio:Title",B_STRING_TYPE,0,gCDData.TrackAt(i),
									strlen(gCDData.TrackAt(i))+1);
					node.WriteAttr("Audio:Album",B_STRING_TYPE,0,gCDData.Album(),
									strlen(gCDData.Album())+1);
					node.WriteAttr("Audio:Artist",B_STRING_TYPE,0,gCDData.Artist(),
									strlen(gCDData.Artist())+1);
					
					int32 tracknum = i+1;
					node.WriteAttr("Audio:Track",B_INT32_TYPE,0,(const void *)&tracknum, sizeof(int32));
					
					node.WriteAttr("Audio:Bitrate",B_STRING_TYPE,0,(const void *)"128", strlen("128")+1);
					
					cdaudio_time tracktime;
					if (gCDDrive.GetTimeForTrack(i+1,tracktime))
					{
						char timestring[20];
						
						// The only time when we will ever get this far when ripstat != B_OK is if
						// we have issues related to misreported track times on an enhanced CD. In this
						// case, we make use of the riptime variable declared above to find out
						// just how much was actually ripped in order to write the proper playing time
						if (ripstat!=B_OK)
							sprintf(timestring,"%.2ld:%.2ld",rippedtime.minutes,rippedtime.seconds);
						else
							sprintf(timestring,"%.2ld:%.2ld",tracktime.minutes,tracktime.seconds);
						node.WriteAttr("Audio:Length",B_STRING_TYPE,0,timestring, strlen(timestring)+1);
					}
				}
				
				// write the file's tags
				BString inString;
				
				syscmd = "tagwriter ";
				inString = gCDData.TrackAt(i);
				inString.CharacterEscape(FILE_ESCAPE_CHARACTERS,'\\');
				syscmd << "-t " << inString;
				
				inString = gCDData.Artist();
				inString.CharacterEscape("<> '\"\\|?[]{}():;`,",'\\');
				syscmd << " -a " << inString;
				
				if (strlen(gCDData.Genre())>0)
				{
					inString = gCDData.Genre();
					inString.CharacterEscape(FILE_ESCAPE_CHARACTERS,'\\');
					syscmd << " -g " << inString;
				}
				
				if (strlen(gCDData.Album())>0)
				{
					inString = gCDData.Album();
					inString.CharacterEscape(FILE_ESCAPE_CHARACTERS,'\\');
					syscmd << " -A " << inString;
				}
				
				syscmd << " -T " << (i+1) << " ";
				syscmd << " -c 'Created by SimplyVorbis' ";
				
				inString = ripname;
				inString.ReplaceAll("/", " - ");
				inString.CharacterEscape(FILE_ESCAPE_CHARACTERS,'\\');
				syscmd+=inString;
				
				//printf("Tag command: %s\n",syscmd.String());
				system(syscmd.String());
				
				// Move the file to the real destination
				BEntry entry(ripname.String());
#ifdef FAKE_RIPPING
				{
					{
#else
				if (entry.Exists())
				{
					BDirectory destination(destfolder.String());
				
					// overwrite an existing file - allow re-ripping a file :)
					if (entry.MoveTo(&destination,NULL,true)!=B_OK)
					{
#endif
						// chances are that if this failed, it's because the destination
						// path is not on the same volume
						view->Window()->Lock();
						BString out(_T("Copying to "));
						out << destfolder;
						view->fProgressBar->SetText(out.String());
						view->Window()->Unlock();
						
						BString cmddest(destfolder);
						cmddest.CharacterEscape("<> '\"\\|[]{}():;`,",'\\');
						
						// *sigh* Certain characters are not allowed for FAT32 names.
						if (!write_attributes)
						{
							cmddest.RemoveAll("?");
							syscmd="cp -fp ";
							syscmd << inString << " " << cmddest;
						}
						else
						{
							syscmd = "copyattr -d ";
							syscmd << inString << " " << cmddest;
						}
						
						//printf("Copy command: %s\n",syscmd.String());
						if (system(syscmd.String())!=0)
							copyfailed=true;
						
						if (!copyfailed)
						{
							entry.Remove();
							syscmd = "settype -t \"audio/x-vorbis\" ";
							syscmd << cmddest << inString;
							system(syscmd.String());
							printf("type command: %s\n", syscmd.String());
						}
						else
						{
							copyfailed=false;
							showfailalert++;
						}
					}
					BString playlistentry(destfolder.String());
					playlistentry << ripname << "\n";
					playlistfile.Write(playlistentry.String(),playlistentry.Length());
				}
			}
		}
		
		gCDDrive.Stop();

#ifndef FAKE_RIPPING		
		// This will show the alert once in the ripping process, as opposed to after every track.
		if (showfailalert == 1)
		{
			copyfailed=false;
			#ifdef SYS_ZETA
			BAlert *alert = new BAlert("SimplyVorbis",_T("RIpViewMultiline4"),_T("OK"));
			#else
			BAlert *alert = new BAlert("SimplyVorbis","SimplyVorbis ran into unexpected issues copying your "
				"music files to the music folder. They have not been lost, however. After clicking "
				"OK, a window will pop up and you can move them to wherever you want.","OK");
			#endif
			alert->Go();
			
			system("/boot/beos/system/Tracker . &");
		}
#endif

	}
	
	if (make_playlist)
	{
		BNodeInfo nodeinfo(&playlistfile);
		if (nodeinfo.InitCheck()==B_OK)
			nodeinfo.SetType("text/x-playlist");
		playlistfile.Unset();
	}
	
	view->Window()->Lock();
	view->fProgressBar->SetText(_T("Finished."));
	view->Window()->Unlock();
	snooze(1000000);
	
	view->fRipThread = -1;
	view->Window()->PostMessage(M_STOP_ENCODING);
	
	release_sem(abort_thread);
	return 0;
}