wxString GetGenreNum(int i) { #ifdef USE_LIBID3TAG id3_latin1_t i_latin1[50]; id3_ucs4_t *i_ucs4; const id3_ucs4_t *genre_ucs4; sprintf((char *)i_latin1, "%d", i); i_ucs4 = (id3_ucs4_t *)malloc((id3_latin1_length(i_latin1) + 1) * sizeof(*i_ucs4)); if (i_ucs4) { id3_latin1_decode(i_latin1, i_ucs4); genre_ucs4 = id3_genre_name(i_ucs4); char *genre_char = (char *)id3_ucs4_utf8duplicate(genre_ucs4); wxString genreStr = UTF8CTOWX(genre_char); free(genre_char); free(i_ucs4); return genreStr; } #endif // ifdef USE_LIBID3TAG return wxT(""); }
bool XMLTagHandler::ReadXMLTag(const char *tag, const char **attrs) { wxArrayString tmp_attrs; while (*attrs) { const char *s = *attrs++; tmp_attrs.Add(UTF8CTOWX(s)); } // JKC: Previously the next line was: // const char **out_attrs = new char (const char *)[tmp_attrs.GetCount()+1]; // however MSVC doesn't like the constness in this position, so this is now // added by a cast after creating the array of pointers-to-non-const chars. const wxChar **out_attrs = (const wxChar**)new wxChar *[tmp_attrs.GetCount()+1]; for (size_t i=0; i<tmp_attrs.GetCount(); i++) { out_attrs[i] = tmp_attrs[i].c_str(); } out_attrs[tmp_attrs.GetCount()] = 0; bool result = HandleXMLTag(UTF8CTOWX(tag).c_str(), out_attrs); delete[] out_attrs; return result; }
void ODFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata) { switch (metadata->type) { case FLAC__METADATA_TYPE_VORBIS_COMMENT: for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { mComments.Add(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry)); } break; case FLAC__METADATA_TYPE_STREAMINFO: mDecoder->mSampleRate=metadata->data.stream_info.sample_rate; mDecoder->mNumChannels=metadata->data.stream_info.channels; mDecoder->mBitsPerSample=metadata->data.stream_info.bits_per_sample; mDecoder->mNumSamples=metadata->data.stream_info.total_samples; if (mDecoder->mBitsPerSample<=16) { if (mDecoder->mFormat<int16Sample) { mDecoder->mFormat=int16Sample; } } else if (mDecoder->mBitsPerSample<=24) { if (mDecoder->mFormat<int24Sample) { mDecoder->mFormat=int24Sample; } } else { mDecoder->mFormat=floatSample; } mDecoder->mStreamInfoDone=true; break; // handle the other types we do nothing with to avoid a warning case FLAC__METADATA_TYPE_PADDING: // do nothing with padding case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels? case FLAC__METADATA_TYPE_PICTURE: // ignore pictures case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either case FLAC__MAX_METADATA_TYPE: // quiet compiler warning with this line break; } }
wxString GetID3FieldStr(struct id3_tag *tp, const char *name) { struct id3_frame *frame; frame = id3_tag_findframe(tp, name, 0); if (frame) { const id3_ucs4_t *ustr; if (strcmp(name, ID3_FRAME_COMMENT) == 0) ustr = id3_field_getfullstring(&frame->fields[3]); else ustr = id3_field_getstrings(&frame->fields[1], 0); if (ustr) { char *str = (char *)id3_ucs4_utf8duplicate(ustr); wxString s = UTF8CTOWX(str); free(str); return s; } } return wxT(""); }
bool OggImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks, int *outNumTracks, Tags *tags) { wxASSERT(mFile->IsOpened()); /* -1 is for the current logical bitstream */ vorbis_info *vi = ov_info(mVorbisFile, -1); vorbis_comment *vc = ov_comment(mVorbisFile, -1); WaveTrack **channels = new WaveTrack *[vi->channels]; int c; for (c = 0; c < vi->channels; c++) { channels[c] = trackFactory->NewWaveTrack(int16Sample, vi->rate); if (vi->channels == 2) { switch (c) { case 0: channels[c]->SetChannel(Track::LeftChannel); channels[c]->SetLinked(true); break; case 1: channels[c]->SetChannel(Track::RightChannel); channels[c]->SetTeamed(true); break; } } else { channels[c]->SetChannel(Track::MonoChannel); } } /* The number of bytes to get from the codec in each run */ #define CODEC_TRANSFER_SIZE 4096 /* The number of samples to read between calls to the callback. * Balance between responsiveness of the GUI and throughput of import. */ #define SAMPLES_PER_CALLBACK 100000 short *mainBuffer = new short[CODEC_TRANSFER_SIZE]; /* determine endianness (clever trick courtesy of Nicholas Devillard, * (http://www.eso.org/~ndevilla/endian/) */ int testvar = 1, endian; if(*(char *)&testvar) endian = 0; // little endian else endian = 1; // big endian /* number of samples currently in each channel's buffer */ bool cancelled = false; long bytesRead = 0; long samplesRead = 0; int bitstream = 0; int samplesSinceLastCallback = 0; // You would think that the stream would already be seeked to 0, and // indeed it is if the file is legit. But I had several ogg files on // my hard drive that have malformed headers, and this added call // causes them to be read correctly. Otherwise they have lots of // zeros inserted at the beginning ov_pcm_seek(mVorbisFile, 0); do { /* get data from the decoder */ bytesRead = ov_read(mVorbisFile, (char *) mainBuffer, CODEC_TRANSFER_SIZE, endian, 2, // word length (2 for 16 bit samples) 1, // signed &bitstream); if (bytesRead < 0) { /* Malformed Ogg Vorbis file. */ /* TODO: Return some sort of meaningful error. */ break; } samplesRead = bytesRead / vi->channels / sizeof(short); /* give the data to the wavetracks */ for (c = 0; c < vi->channels; c++) channels[c]->Append((char *)(mainBuffer + c), int16Sample, samplesRead, vi->channels); samplesSinceLastCallback += samplesRead; if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) { if( mProgressCallback ) cancelled = mProgressCallback(mUserData, ov_time_tell(mVorbisFile) / ov_time_total(mVorbisFile, bitstream)); samplesSinceLastCallback -= SAMPLES_PER_CALLBACK; } } while (!cancelled && bytesRead != 0 && bitstream == 0); delete[]mainBuffer; bool res = (!cancelled && bytesRead >= 0); if (!res) { for(c = 0; c < vi->channels; c++) { delete channels[c]; } delete[] channels; return false; } *outNumTracks = vi->channels; *outTracks = new Track *[vi->channels]; for (c = 0; c < vi->channels; c++) { channels[c]->Flush(); (*outTracks)[c] = channels[c]; } delete[] channels; if (vc) { tags->Clear(); for (c = 0; c < vc->comments; c++) { wxString comment = UTF8CTOWX(vc->user_comments[c]); tags->SetTag(comment.BeforeFirst(wxT('=')), comment.AfterFirst(wxT('='))); } } return true; }
int OggImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks, int *outNumTracks, Tags *tags) { wxASSERT(mFile->IsOpened()); CreateProgress(); //Number of streams used may be less than mVorbisFile->links, //but this way bitstream matches array index. mChannels = new WaveTrack **[mVorbisFile->links]; int i,c; for (i = 0; i < mVorbisFile->links; i++) { //Stream is not used if (mStreamUsage[i] == 0) { //This is just a padding to keep bitstream number and //array indices matched. mChannels[i] = NULL; continue; } vorbis_info *vi = ov_info(mVorbisFile, i); vorbis_comment *vc = ov_comment(mVorbisFile, i); mChannels[i] = new WaveTrack *[vi->channels]; for (c = 0; c < vi->channels; c++) { mChannels[i][c] = trackFactory->NewWaveTrack(int16Sample, vi->rate); if (vi->channels == 2) { switch (c) { case 0: mChannels[i][c]->SetChannel(Track::LeftChannel); mChannels[i][c]->SetLinked(true); break; case 1: mChannels[i][c]->SetChannel(Track::RightChannel); mChannels[i][c]->SetTeamed(true); break; } } else { mChannels[i][c]->SetChannel(Track::MonoChannel); } } } /* The number of bytes to get from the codec in each run */ #define CODEC_TRANSFER_SIZE 4096 /* The number of samples to read between calls to the callback. * Balance between responsiveness of the GUI and throughput of import. */ #define SAMPLES_PER_CALLBACK 100000 short *mainBuffer = new short[CODEC_TRANSFER_SIZE]; /* determine endianness (clever trick courtesy of Nicholas Devillard, * (http://www.eso.org/~ndevilla/endian/) */ int testvar = 1, endian; if(*(char *)&testvar) endian = 0; // little endian else endian = 1; // big endian /* number of samples currently in each channel's buffer */ bool cancelled = false; long bytesRead = 0; long samplesRead = 0; int bitstream = 0; int samplesSinceLastCallback = 0; // You would think that the stream would already be seeked to 0, and // indeed it is if the file is legit. But I had several ogg files on // my hard drive that have malformed headers, and this added call // causes them to be read correctly. Otherwise they have lots of // zeros inserted at the beginning ov_pcm_seek(mVorbisFile, 0); do { /* get data from the decoder */ bytesRead = ov_read(mVorbisFile, (char *) mainBuffer, CODEC_TRANSFER_SIZE, endian, 2, // word length (2 for 16 bit samples) 1, // signed &bitstream); if (bytesRead < 0) { /* Malformed Ogg Vorbis file. */ /* TODO: Return some sort of meaningful error. */ break; } samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short); /* give the data to the wavetracks */ if (mStreamUsage[bitstream] != 0) { for (c = 0; c < mVorbisFile->vi[bitstream].channels; c++) mChannels[bitstream][c]->Append((char *)(mainBuffer + c), int16Sample, samplesRead, mVorbisFile->vi[bitstream].channels); } samplesSinceLastCallback += samplesRead; if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) { cancelled = !mProgress->Update(ov_time_tell(mVorbisFile), ov_time_total(mVorbisFile, bitstream)); samplesSinceLastCallback -= SAMPLES_PER_CALLBACK; } } while (!cancelled && bytesRead != 0); delete[]mainBuffer; bool res = (!cancelled && bytesRead >= 0); if (!res) { for (i = 0; i < mVorbisFile->links; i++) { if (mChannels[i]) { for(c = 0; c < mVorbisFile->vi[bitstream].channels; c++) { if (mChannels[i][c]) delete mChannels[i][c]; } delete[] mChannels[i]; } } delete[] mChannels; return (cancelled ? eImportCancelled : eImportFailed); } *outNumTracks = 0; for (int s = 0; s < mVorbisFile->links; s++) { if (mStreamUsage[s] != 0) *outNumTracks += mVorbisFile->vi[s].channels; } *outTracks = new Track *[*outNumTracks]; int trackindex = 0; for (i = 0; i < mVorbisFile->links; i++) { if (mChannels[i]) { for (c = 0; c < mVorbisFile->vi[i].channels; c++) { mChannels[i][c]->Flush(); (*outTracks)[trackindex++] = mChannels[i][c]; } delete[] mChannels[i]; } } delete[] mChannels; //\todo { Extract comments from each stream? } if (mVorbisFile->vc[0].comments > 0) { tags->Clear(); for (c = 0; c < mVorbisFile->vc[0].comments; c++) { wxString comment = UTF8CTOWX(mVorbisFile->vc[0].user_comments[c]); wxString name = comment.BeforeFirst(wxT('=')); wxString value = comment.AfterFirst(wxT('=')); if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) { long val; if (value.Length() == 4 && value.ToLong(&val)) { name = TAG_YEAR; } } tags->SetTag(name, value); } } return eImportSuccess; }
XMLTagHandler *XMLTagHandler::ReadXMLChild(const char *tag) { return HandleXMLChild(UTF8CTOWX(tag).c_str()); }
void XMLTagHandler::ReadXMLEndTag(const char *tag) { HandleXMLEndTag(UTF8CTOWX(tag).c_str()); }
void MP3ImportFileHandle::ImportID3(Tags *tags) { #ifdef USE_LIBID3TAG struct id3_file *fp = id3_file_open(OSFILENAME(mFilename), ID3_FILE_MODE_READONLY); if (!fp) { return; } struct id3_tag *tp = id3_file_tag(fp); if (!tp) { id3_file_close(fp); return; } tags->Clear(); tags->SetID3V2( tp->options & ID3_TAG_OPTION_ID3V1 ? false : true ); // Loop through all frames for (int i = 0; i < (int) tp->nframes; i++) { struct id3_frame *frame = tp->frames[i]; // printf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id); // printf("Desc: %s\n", frame->description); // printf("Num fields: %d\n", frame->nfields); // for (int j = 0; j < (int) frame->nfields; j++) { // printf("field %d type %d\n", j, frame->fields[j].type ); // if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) { // printf("num strings %d\n", frame->fields[j].stringlist.nstrings); // } // } wxString n, v; // Determine the tag name if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) { n = TAG_TITLE; } else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) { n = TAG_ARTIST; } else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) { n = TAG_ALBUM; } else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) { n = TAG_TRACK; } else if (strcmp(frame->id, "TYER") == 0) { n = TAG_YEAR; } else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) { n = TAG_YEAR; } else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) { n = TAG_COMMENTS; } else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) { n = TAG_GENRE; } else { // Use frame description as default tag name. The descriptions // may include several "meanings" separated by "/" characters, so // we just use the first meaning n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/')); } const id3_ucs4_t *ustr = NULL; if (n == TAG_COMMENTS) { ustr = id3_field_getfullstring(&frame->fields[3]); } else if (frame->nfields == 3) { ustr = id3_field_getstring(&frame->fields[1]); if (ustr) { char *str = (char *)id3_ucs4_utf8duplicate(ustr); n = UTF8CTOWX(str); free(str); } ustr = id3_field_getstring(&frame->fields[2]); } else if (frame->nfields >= 2) { ustr = id3_field_getstrings(&frame->fields[1], 0); } if (ustr) { char *str = (char *)id3_ucs4_utf8duplicate(ustr); v = UTF8CTOWX(str); free(str); } if (!n.IsEmpty() && !v.IsEmpty()) { tags->SetTag(n, v); } } // Convert v1 genre to name if (tags->HasTag(TAG_GENRE)) { long g = -1; if (tags->GetTag(TAG_GENRE).ToLong(&g)) { tags->SetTag(TAG_GENRE, tags->GetGenre(g)); } } id3_file_close(fp); #endif // ifdef USE_LIBID3TAG }
void MP3ImportFileHandle::ImportID3(Tags *tags) { #ifdef USE_LIBID3TAG wxFile f; // will be closed when it goes out of scope struct id3_file *fp = NULL; if (f.Open(mFilename)) { // Use id3_file_fdopen() instead of id3_file_open since wxWidgets can open a // file with a Unicode name and id3_file_open() can't (under Windows). fp = id3_file_fdopen(f.fd(), ID3_FILE_MODE_READONLY); } if (!fp) { return; } // The file descriptor is now owned by "fp", so we must tell "f" to forget // about it. f.Detach(); struct id3_tag *tp = id3_file_tag(fp); if (!tp) { id3_file_close(fp); return; } tags->Clear(); // Loop through all frames bool have_year = false; for (int i = 0; i < (int) tp->nframes; i++) { struct id3_frame *frame = tp->frames[i]; // printf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id); // printf("Desc: %s\n", frame->description); // printf("Num fields: %d\n", frame->nfields); // for (int j = 0; j < (int) frame->nfields; j++) { // printf("field %d type %d\n", j, frame->fields[j].type ); // if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) { // printf("num strings %d\n", frame->fields[j].stringlist.nstrings); // } // } wxString n, v; // Determine the tag name if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) { n = TAG_TITLE; } else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) { n = TAG_ARTIST; } else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) { n = TAG_ALBUM; } else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) { n = TAG_TRACK; } else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) { // LLL: When libid3tag encounters the "TYER" tag, it converts it to a // "ZOBS" (obsolete) tag and adds a "TDRC" tag at the end of the // list of tags using the first 4 characters of the "TYER" tag. // Since we write both the "TDRC" and "TYER" tags, the "TDRC" tag // will always be encountered first in the list. We want use it // since the converted "TYER" tag may have been truncated. if (have_year) { continue; } n = TAG_YEAR; have_year = true; } else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) { n = TAG_COMMENTS; } else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) { n = TAG_GENRE; } else { // Use frame description as default tag name. The descriptions // may include several "meanings" separated by "/" characters, so // we just use the first meaning n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/')); } const id3_ucs4_t *ustr = NULL; if (n == TAG_COMMENTS) { ustr = id3_field_getfullstring(&frame->fields[3]); } else if (frame->nfields == 3) { ustr = id3_field_getstring(&frame->fields[1]); if (ustr) { char *str = (char *)id3_ucs4_utf8duplicate(ustr); n = UTF8CTOWX(str); free(str); } ustr = id3_field_getstring(&frame->fields[2]); } else if (frame->nfields >= 2) { ustr = id3_field_getstrings(&frame->fields[1], 0); } if (ustr) { char *str = (char *)id3_ucs4_utf8duplicate(ustr); v = UTF8CTOWX(str); free(str); } if (!n.IsEmpty() && !v.IsEmpty()) { tags->SetTag(n, v); } } // Convert v1 genre to name if (tags->HasTag(TAG_GENRE)) { long g = -1; if (tags->GetTag(TAG_GENRE).ToLong(&g)) { tags->SetTag(TAG_GENRE, tags->GetGenre(g)); } } id3_file_close(fp); #endif // ifdef USE_LIBID3TAG }
bool EffectNyquist::ProcessOne() { nyx_rval rval; if (GetEffectFlags() & INSERT_EFFECT) { nyx_set_audio_params(mCurTrack[0]->GetRate(), 0); } else { nyx_set_audio_params(mCurTrack[0]->GetRate(), mCurLen); nyx_set_input_audio(StaticGetCallback, (void *)this, mCurNumChannels, mCurLen, mCurTrack[0]->GetRate()); } wxString cmd; if (mDebug) { cmd += wxT("(setf *tracenable* T)\n"); if (mExternal) { cmd += wxT("(setf *breakenable* T)\n"); } } for (unsigned int j = 0; j < mControls.GetCount(); j++) { if (mControls[j].type == NYQ_CTRL_REAL) { // We use Internat::ToString() rather than "%f" here because we // always have to use the dot as decimal separator when giving // numbers to Nyquist, whereas using "%f" will use the user's // decimal separator which may be a comma in some countries. cmd += wxString::Format(wxT("(setf %s %s)\n"), mControls[j].var.c_str(), Internat::ToString(mControls[j].val).c_str()); } else if (mControls[j].type == NYQ_CTRL_INT || mControls[j].type == NYQ_CTRL_CHOICE) { cmd += wxString::Format(wxT("(setf %s %d)\n"), mControls[j].var.c_str(), (int)(mControls[j].val)); } else if (mControls[j].type == NYQ_CTRL_STRING) { wxString str = mControls[j].valStr; str.Replace(wxT("\\"), wxT("\\\\")); str.Replace(wxT("\""), wxT("\\\"")); cmd += wxT("(setf "); // restrict variable names to 7-bit ASCII: cmd += mControls[j].var.c_str(); cmd += wxT(" \""); cmd += str; // unrestricted value will become quoted UTF-8 cmd += wxT("\")\n"); } } if (mIsSal) { wxString str = mCmd; str.Replace(wxT("\\"), wxT("\\\\")); str.Replace(wxT("\""), wxT("\\\"")); // this is tricky: we need SAL to call main so that we can get a // SAL traceback in the event of an error (sal-compile catches the // error and calls sal-error-output), but SAL does not return values. // We will catch the value in a special global aud:result and if no // error occurs, we will grab the value with a LISP expression str += wxT("\nset aud:result = main()\n"); if (mDebug) { // since we're about to evaluate SAL, remove LISP trace enable and // break enable (which stops SAL processing) and turn on SAL stack // trace cmd += wxT("(setf *tracenable* nil)\n"); cmd += wxT("(setf *breakenable* nil)\n"); cmd += wxT("(setf *sal-traceback* t)\n"); } if (mCompiler) { cmd += wxT("(setf *sal-compiler-debug* t)\n"); } cmd += wxT("(setf *sal-call-stack* nil)\n"); // if we do not set this here and an error occurs in main, another // error will be raised when we try to return the value of aud:result // which is unbound cmd += wxT("(setf aud:result nil)\n"); cmd += wxT("(sal-compile-audacity \"") + str + wxT("\" t t nil)\n"); // Capture the value returned by main (saved in aud:result), but // set aud:result to nil so sound results can be evaluated without // retaining audio in memory cmd += wxT("(prog1 aud:result (setf aud:result nil))\n"); } else { cmd += mCmd; } int i; for (i = 0; i < mCurNumChannels; i++) { mCurBuffer[i] = NULL; } rval = nyx_eval_expression(cmd.mb_str(wxConvUTF8)); if (rval == nyx_string) { wxMessageBox(NyquistToWxString(nyx_get_string()), wxT("Nyquist"), wxOK | wxCENTRE, mParent); return true; } if (rval == nyx_double) { wxString str; str.Printf(_("Nyquist returned the value:") + wxString(wxT(" %f")), nyx_get_double()); wxMessageBox(str, wxT("Nyquist"), wxOK | wxCENTRE, mParent); return true; } if (rval == nyx_int) { wxString str; str.Printf(_("Nyquist returned the value:") + wxString(wxT(" %d")), nyx_get_int()); wxMessageBox(str, wxT("Nyquist"), wxOK | wxCENTRE, mParent); return true; } if (rval == nyx_labels) { unsigned int numLabels = nyx_get_num_labels(); unsigned int l; LabelTrack *ltrack = NULL; TrackListIterator iter(mOutputTracks); for (Track *t = iter.First(); t; t = iter.Next()) { if (t->GetKind() == Track::Label) { ltrack = (LabelTrack *)t; break; } } if (!ltrack) { ltrack = mFactory->NewLabelTrack(); this->AddToOutputTracks((Track *)ltrack); } for (l = 0; l < numLabels; l++) { double t0, t1; const char *str; nyx_get_label(l, &t0, &t1, &str); ltrack->AddLabel(t0 + mT0, t1 + mT0, UTF8CTOWX(str)); } return true; } if (rval != nyx_audio) { wxMessageBox(_("Nyquist did not return audio.\n"), wxT("Nyquist"), wxOK | wxCENTRE, mParent); return false; } int outChannels; outChannels = nyx_get_audio_num_channels(); if (outChannels > mCurNumChannels) { wxMessageBox(_("Nyquist returned too many audio channels.\n"), wxT("Nyquist"), wxOK | wxCENTRE, mParent); return false; } double rate = mCurTrack[0]->GetRate(); for (i = 0; i < outChannels; i++) { sampleFormat format = mCurTrack[i]->GetSampleFormat(); if (outChannels == mCurNumChannels) { rate = mCurTrack[i]->GetRate(); } mOutputTrack[i] = mFactory->NewWaveTrack(format, rate); mCurBuffer[i] = NULL; } int success = nyx_get_audio(StaticPutCallback, (void *)this); if (!success) { for(i = 0; i < outChannels; i++) { delete mOutputTrack[i]; mOutputTrack[i] = NULL; } return false; } for (i = 0; i < outChannels; i++) { mOutputTrack[i]->Flush(); if (mCurBuffer[i]) { DeleteSamples(mCurBuffer[i]); } mOutputTime = mOutputTrack[i]->GetEndTime(); } for (i = 0; i < mCurNumChannels; i++) { WaveTrack *out; if (outChannels == mCurNumChannels) { out = mOutputTrack[i]; } else { out = mOutputTrack[0]; } mCurTrack[i]->ClearAndPaste(mT0, mT1, out, false, false); // If we were first in the group adjust non-selected group tracks if (mFirstInGroup) { SyncLockedTracksIterator git(mOutputTracks); Track *t; for (t = git.First(mCurTrack[i]); t; t = git.Next()) { if (!t->GetSelected() && t->IsSyncLockSelected()) { t->SyncLockAdjust(mT1, mT0 + out->GetEndTime()); } } } // Only the first channel can be first in its group mFirstInGroup = false; } for (i = 0; i < outChannels; i++) { delete mOutputTrack[i]; mOutputTrack[i] = NULL; } return true; }
void Tags::ImportID3(wxString fileName) { #ifdef USE_LIBID3TAG struct id3_file *fp = id3_file_open(OSFILENAME(fileName), ID3_FILE_MODE_READONLY); if (!fp) return; struct id3_tag *tp = id3_file_tag(fp); if (!tp) return; mTitle = GetID3FieldStr(tp, ID3_FRAME_TITLE); mArtist = GetID3FieldStr(tp, ID3_FRAME_ARTIST); mAlbum = GetID3FieldStr(tp, ID3_FRAME_ALBUM); mYear = GetID3FieldStr(tp, ID3_FRAME_YEAR); mComments = GetID3FieldStr(tp, ID3_FRAME_COMMENT); long l; wxString s; if ((s = GetID3FieldStr(tp, ID3_FRAME_TRACK)).ToLong(&l)) mTrackNum = l; mID3V2 = ( tp->options & ID3_TAG_OPTION_ID3V1 ) ? false : true; s = GetID3FieldStr(tp, ID3_FRAME_GENRE); if( mID3V2 ) { int numGenres = GetNumGenres(); for(int i=0; i<numGenres; i++) if (0 == s.CmpNoCase(GetGenreNum(i))) mGenre = i; } else { if( s.ToLong( &l ) ) mGenre = l; } // Loop through all remaining frames int i; for(i=0; i<(int)tp->nframes; i++) { struct id3_frame *frame = tp->frames[i]; //printf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id); //printf("Desc: %s\n", frame->description); //printf("Num fields: %d\n", frame->nfields); if (!strcmp(frame->id, ID3_FRAME_TITLE) || !strcmp(frame->id, ID3_FRAME_ARTIST) || !strcmp(frame->id, ID3_FRAME_ALBUM) || !strcmp(frame->id, ID3_FRAME_YEAR) || !strcmp(frame->id, ID3_FRAME_COMMENT) || !strcmp(frame->id, ID3_FRAME_GENRE) || !strcmp(frame->id, ID3_FRAME_TRACK)) { continue; } const id3_ucs4_t *ustr; if (frame->nfields>=2) { ustr = id3_field_getstrings(&frame->fields[1], 0); if (ustr) { wxString name = UTF8CTOWX(frame->description); char *str = (char *)id3_ucs4_utf8duplicate(ustr); wxString value = UTF8CTOWX(str); free(str); mExtraNames.Add(name); mExtraValues.Add(value); } } if (frame->nfields==3) { wxString name, value; ustr = id3_field_getstring(&frame->fields[2]); if (ustr) { char *str = (char *)id3_ucs4_utf8duplicate(ustr); value = UTF8CTOWX(str); free(str); } ustr = id3_field_getstring(&frame->fields[1]); if (ustr) { char *str = (char *)id3_ucs4_utf8duplicate(ustr); name = UTF8CTOWX(str); free(str); } mExtraNames.Add(name); mExtraValues.Add(value); } } id3_file_close(fp); #endif // ifdef USE_LIBID3TAG }
int PCMImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks, int *outNumTracks, Tags *tags) { wxASSERT(mFile); CreateProgress(); WaveTrack **channels = new WaveTrack *[mInfo.channels]; int c; for (c = 0; c < mInfo.channels; c++) { channels[c] = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate); if (mInfo.channels > 1) switch (c) { case 0: channels[c]->SetChannel(Track::LeftChannel); break; case 1: channels[c]->SetChannel(Track::RightChannel); break; default: channels[c]->SetChannel(Track::MonoChannel); } } if (mInfo.channels == 2) { channels[0]->SetLinked(true); channels[1]->SetTeamed(true); } sampleCount fileTotalFrames = (sampleCount)mInfo.frames; sampleCount maxBlockSize = channels[0]->GetMaxBlockSize(); bool cancelled = false; wxString copyEdit = gPrefs->Read(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("edit")); // Fall back to "edit" if it doesn't match anything else bool doEdit = true; if (copyEdit.IsSameAs(wxT("copy"), false)) doEdit = false; // If the format is not seekable, we must use 'copy' mode, // because 'edit' mode depends on the ability to seek to an // arbitrary location in the file. if (!mInfo.seekable) doEdit = false; if (doEdit) { wxLogDebug(wxT("Importing PCM Start\n")); // If this mode has been selected, we form the tracks as // aliases to the files we're editing, i.e. ("foo.wav", 12000-18000) // instead of actually making fresh copies of the samples. for (sampleCount i = 0; i < fileTotalFrames; i += maxBlockSize) { sampleCount blockLen = maxBlockSize; if (i + blockLen > fileTotalFrames) blockLen = fileTotalFrames - i; for (c = 0; c < mInfo.channels; c++) channels[c]->AppendAlias(mFilename, i, blockLen, c); cancelled = !mProgress->Update(i, fileTotalFrames); if (cancelled) break; } #ifdef EXPERIMENTAL_ONDEMAND //now go over the wavetrack/waveclip/sequence and load all the blockfiles into a ComputeSummaryTask. //Add this task to the ODManager and the Track itself. wxLogDebug(wxT("Importing PCM \n")); for (c = 0; c < mInfo.channels; c++) { ODComputeSummaryTask* computeTask; computeTask=new ODComputeSummaryTask; computeTask->SetWaveTrack(channels[c]); ODManager::Instance()->AddTaskToWaveTrack(computeTask,channels[c]); } #endif } else { // Otherwise, we're in the "copy" mode, where we read in the actual // samples from the file and store our own local copy of the // samples in the tracks. samplePtr srcbuffer = NewSamples(maxBlockSize * mInfo.channels, mFormat); samplePtr buffer = NewSamples(maxBlockSize, mFormat); unsigned long framescompleted = 0; long block; do { block = maxBlockSize; if (mFormat == int16Sample) block = sf_readf_short(mFile, (short *)srcbuffer, block); else block = sf_readf_float(mFile, (float *)srcbuffer, block); if (block) { for(c=0; c<mInfo.channels; c++) { if (mFormat==int16Sample) { for(int j=0; j<block; j++) ((short *)buffer)[j] = ((short *)srcbuffer)[mInfo.channels*j+c]; } else { for(int j=0; j<block; j++) ((float *)buffer)[j] = ((float *)srcbuffer)[mInfo.channels*j+c]; } channels[c]->Append(buffer, mFormat, block); } framescompleted += block; } cancelled = !mProgress->Update((long long unsigned)framescompleted, (long long unsigned)fileTotalFrames); if (cancelled) break; } while (block > 0); } if (cancelled) { for (c = 0; c < mInfo.channels; c++) delete channels[c]; delete[] channels; return eImportCancelled; } *outNumTracks = mInfo.channels; *outTracks = new Track *[mInfo.channels]; for(c = 0; c < mInfo.channels; c++) { channels[c]->Flush(); (*outTracks)[c] = channels[c]; } delete[] channels; const char *str; str = sf_get_string(mFile, SF_STR_TITLE); if (str) { tags->SetTag(TAG_TITLE, UTF8CTOWX(str)); } str = sf_get_string(mFile, SF_STR_ARTIST); if (str) { tags->SetTag(TAG_ARTIST, UTF8CTOWX(str)); } str = sf_get_string(mFile, SF_STR_COMMENT); if (str) { tags->SetTag(TAG_COMMENTS, UTF8CTOWX(str)); } str = sf_get_string(mFile, SF_STR_DATE); if (str) { tags->SetTag(TAG_YEAR, UTF8CTOWX(str)); } str = sf_get_string(mFile, SF_STR_COPYRIGHT); if (str) { tags->SetTag(wxT("Copyright"), UTF8CTOWX(str)); } str = sf_get_string(mFile, SF_STR_SOFTWARE); if (str) { tags->SetTag(wxT("Software"), UTF8CTOWX(str)); } return eImportSuccess; }
int OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) { outTracks.clear(); wxASSERT(mFile->IsOpened()); CreateProgress(); //Number of streams used may be less than mVorbisFile->links, //but this way bitstream matches array index. mChannels.resize(mVorbisFile->links); int i = -1; for (auto &link: mChannels) { ++i; //Stream is not used if (mStreamUsage[i] == 0) { //This is just a padding to keep bitstream number and //array indices matched. continue; } vorbis_info *vi = ov_info(mVorbisFile, i); link.resize(vi->channels); int c = - 1; for (auto &channel : link) { ++c; channel = trackFactory->NewWaveTrack(mFormat, vi->rate); if (vi->channels == 2) { switch (c) { case 0: channel->SetChannel(Track::LeftChannel); channel->SetLinked(true); break; case 1: channel->SetChannel(Track::RightChannel); break; } } else { channel->SetChannel(Track::MonoChannel); } } } /* The number of bytes to get from the codec in each run */ #define CODEC_TRANSFER_SIZE 4096 /* The number of samples to read between calls to the callback. * Balance between responsiveness of the GUI and throughput of import. */ #define SAMPLES_PER_CALLBACK 100000 short *mainBuffer = new short[CODEC_TRANSFER_SIZE]; /* determine endianness (clever trick courtesy of Nicholas Devillard, * (http://www.eso.org/~ndevilla/endian/) */ int testvar = 1, endian; if(*(char *)&testvar) endian = 0; // little endian else endian = 1; // big endian /* number of samples currently in each channel's buffer */ int updateResult = eProgressSuccess; long bytesRead = 0; long samplesRead = 0; int bitstream = 0; int samplesSinceLastCallback = 0; // You would think that the stream would already be seeked to 0, and // indeed it is if the file is legit. But I had several ogg files on // my hard drive that have malformed headers, and this added call // causes them to be read correctly. Otherwise they have lots of // zeros inserted at the beginning ov_pcm_seek(mVorbisFile, 0); do { /* get data from the decoder */ bytesRead = ov_read(mVorbisFile, (char *) mainBuffer, CODEC_TRANSFER_SIZE, endian, 2, // word length (2 for 16 bit samples) 1, // signed &bitstream); if (bytesRead == OV_HOLE) { wxFileName ff(mFilename); wxLogError(wxT("Ogg Vorbis importer: file %s is malformed, ov_read() reported a hole"), ff.GetFullName().c_str()); /* http://lists.xiph.org/pipermail/vorbis-dev/2001-February/003223.html * is the justification for doing this - best effort for malformed file, * hence the message. */ continue; } else if (bytesRead < 0) { /* Malformed Ogg Vorbis file. */ /* TODO: Return some sort of meaningful error. */ wxLogError(wxT("Ogg Vorbis importer: ov_read() returned error %i"), bytesRead); break; } samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short); /* give the data to the wavetracks */ auto iter = mChannels.begin(); std::advance(iter, bitstream); if (mStreamUsage[bitstream] != 0) { auto iter2 = iter->begin(); for (int c = 0; c < mVorbisFile->vi[bitstream].channels; ++iter2, ++c) iter2->get()->Append((char *)(mainBuffer + c), int16Sample, samplesRead, mVorbisFile->vi[bitstream].channels); } samplesSinceLastCallback += samplesRead; if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) { updateResult = mProgress->Update(ov_time_tell(mVorbisFile), ov_time_total(mVorbisFile, bitstream)); samplesSinceLastCallback -= SAMPLES_PER_CALLBACK; } } while (updateResult == eProgressSuccess && bytesRead != 0); delete[]mainBuffer; int res = updateResult; if (bytesRead < 0) res = eProgressFailed; if (res == eProgressFailed || res == eProgressCancelled) { return res; } for (auto &link : mChannels) { for (auto &channel : link) { channel->Flush(); outTracks.push_back(std::move(channel)); } } //\todo { Extract comments from each stream? } if (mVorbisFile->vc[0].comments > 0) { tags->Clear(); for (int c = 0; c < mVorbisFile->vc[0].comments; c++) { wxString comment = UTF8CTOWX(mVorbisFile->vc[0].user_comments[c]); wxString name = comment.BeforeFirst(wxT('=')); wxString value = comment.AfterFirst(wxT('=')); if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) { long val; if (value.Length() == 4 && value.ToLong(&val)) { name = TAG_YEAR; } } tags->SetTag(name, value); } } return res; }