libgig  4.4.1
Akai.cpp
1 /*
2  libakai - C++ cross-platform akai sample disk reader
3  Copyright (C) 2002-2003 Sébastien Métrot
4 
5  Linux port by Christian Schoenebeck <cuse@users.sourceforge.net> 2003-2019
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public
9  License as published by the Free Software Foundation; either
10  version 2.1 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 // Akai.cpp
22 
23 #include "Akai.h"
24 
25 #include <stdio.h>
26 #include <string.h>
27 #ifndef _MSC_VER
28 # include <unistd.h>
29 # include <fcntl.h>
30 #endif
31 #if defined(_CARBON_) || defined(__APPLE__)
32 # if defined (__GNUC__) && (__GNUC__ >= 4)
33 # include <sys/disk.h>
34 # else
35 # include <dev/disk.h>
36 # endif
37 #endif
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <errno.h>
41 #if defined(_CARBON_) || defined(__APPLE__)
42 #include <paths.h>
43 #include <sys/ioctl.h>
44 #include <sys/param.h>
45 #include <IOKit/IOKitLib.h>
46 #include <IOKit/IOBSD.h>
47 #include <IOKit/storage/IOMediaBSDClient.h>
48 #include <IOKit/storage/IOMedia.h>
49 #include <IOKit/storage/IOCDMedia.h>
50 #include <IOKit/storage/IOCDTypes.h>
51 #include <CoreFoundation/CoreFoundation.h>
52 #endif
53 
54 #if defined(_CARBON_) || defined(__APPLE__)
55 
56 // These definitions were taken from mount_cd9660.c
57 // There are some similar definitions in IOCDTypes.h
58 // however there seems to be some dissagreement in
59 // the definition of CDTOC.length
60 struct _CDMSF {
61  u_char minute;
62  u_char second;
63  u_char frame;
64 };
65 
66 #define MSF_TO_LBA(msf) \
67  (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
68 
69 struct _CDTOC_Desc {
70  u_char session;
71  u_char ctrl_adr; /* typed to be machine and compiler independent */
72  u_char tno;
73  u_char point;
74  struct _CDMSF address;
75  u_char zero;
76  struct _CDMSF p;
77 };
78 
79 struct _CDTOC {
80  u_short length; /* in native cpu endian */
81  u_char first_session;
82  u_char last_session;
83  struct _CDTOC_Desc trackdesc[1];
84 };
85 
86 // Most of the following Mac CDROM IO functions were taken from Apple's IOKit
87 // examples (BSD style license). ReadTOC() function was taken from the Bochs x86
88 // Emulator (LGPL). Most probably they have taken it however also from some
89 // other BSD style licensed example code as well ...
90 
91 // Returns an iterator across all CD media (class IOCDMedia). Caller is responsible for releasing
92 // the iterator when iteration is complete.
93 static kern_return_t FindEjectableCDMedia(io_iterator_t *mediaIterator) {
94  kern_return_t kernResult;
95  CFMutableDictionaryRef classesToMatch;
96 
97  // CD media are instances of class kIOCDMediaClass
98  classesToMatch = IOServiceMatching(kIOCDMediaClass);
99  if (classesToMatch == NULL) {
100  printf("IOServiceMatching returned a NULL dictionary.\n");
101  } else {
102  CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
103  // Each IOMedia object has a property with key kIOMediaEjectableKey which is true if the
104  // media is indeed ejectable. So add this property to the CFDictionary we're matching on.
105  }
106 
107  kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, mediaIterator);
108 
109  return kernResult;
110 }
111 
112 // Given an iterator across a set of CD media, return the BSD path to the
113 // next one. If no CD media was found the path name is set to an empty string.
114 static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize) {
115  io_object_t nextMedia;
116  kern_return_t kernResult = KERN_FAILURE;
117 
118  *bsdPath = '\0';
119 
120  nextMedia = IOIteratorNext(mediaIterator);
121  if (nextMedia) {
122  CFTypeRef bsdPathAsCFString;
123 
124  bsdPathAsCFString = IORegistryEntryCreateCFProperty(nextMedia,
125  CFSTR(kIOBSDNameKey),
126  kCFAllocatorDefault,
127  0);
128  if (bsdPathAsCFString) {
129  strlcpy(bsdPath, _PATH_DEV, maxPathSize);
130 
131  // Add "r" before the BSD node name from the I/O Registry to specify the raw disk
132  // node. The raw disk nodes receive I/O requests directly and do not go through
133  // the buffer cache.
134 
135  strlcat(bsdPath, "r", maxPathSize);
136 
137  size_t devPathLength = strlen(bsdPath);
138 
139  if (CFStringGetCString((CFStringRef)bsdPathAsCFString,
140  bsdPath + devPathLength,
141  maxPathSize - devPathLength,
142  kCFStringEncodingUTF8)) {
143  printf("BSD path: %s\n", bsdPath);
144  kernResult = KERN_SUCCESS;
145  }
146 
147  CFRelease(bsdPathAsCFString);
148  }
149 
150  IOObjectRelease(nextMedia);
151  }
152 
153  return kernResult;
154 }
155 
156 // Given the path to a CD drive, open the drive.
157 // Return the file descriptor associated with the device.
158 /*static int OpenDrive(const char *bsdPath) {
159  int fileDescriptor;
160 
161  // This open() call will fail with a permissions error if the sample has been changed to
162  // look for non-removable media. This is because device nodes for fixed-media devices are
163  // owned by root instead of the current console user.
164 
165  fileDescriptor = open(bsdPath, O_RDONLY);
166 
167  if (fileDescriptor == -1) {
168  printf("Error opening device %s: ", bsdPath);
169  perror(NULL);
170  }
171 
172  return fileDescriptor;
173 }*/
174 
175 // Given the file descriptor for a whole-media CD device, read a sector from the drive.
176 // Return true if successful, otherwise false.
177 /*static Boolean ReadSector(int fileDescriptor) {
178  char *buffer;
179  ssize_t numBytes;
180  u_int32_t blockSize;
181 
182  // This ioctl call retrieves the preferred block size for the media. It is functionally
183  // equivalent to getting the value of the whole media object's "Preferred Block Size"
184  // property from the IORegistry.
185  if (ioctl(fileDescriptor, DKIOCGETBLOCKSIZE, &blockSize)) {
186  perror("Error getting preferred block size");
187 
188  // Set a reasonable default if we can't get the actual preferred block size. A real
189  // app would probably want to bail at this point.
190  blockSize = kCDSectorSizeCDDA;
191  }
192 
193  printf("Media has block size of %d bytes.\n", blockSize);
194 
195  // Allocate a buffer of the preferred block size. In a real application, performance
196  // can be improved by reading as many blocks at once as you can.
197  buffer = (char*) malloc(blockSize);
198 
199  // Do the read. Note that we use read() here, not fread(), since this is a raw device
200  // node.
201  numBytes = read(fileDescriptor, buffer, blockSize);
202 
203  // Free our buffer. Of course, a real app would do something useful with the data first.
204  free(buffer);
205 
206  return numBytes == blockSize ? true : false;
207 }*/
208 
209 // Given the file descriptor for a device, close that device.
210 /*static void CloseDrive(int fileDescriptor) {
211  close(fileDescriptor);
212 }*/
213 
214 // path is the BSD path to a raw device such as /dev/rdisk1
215 static struct _CDTOC * ReadTOC(const char *devpath) {
216  struct _CDTOC * toc_p = NULL;
217  io_iterator_t iterator = 0;
218  io_registry_entry_t service = 0;
219  CFDictionaryRef properties = 0;
220  CFDataRef data = 0;
221  mach_port_t port = 0;
222  const char *devname;
223 
224  if ((devname = strrchr(devpath, '/')) != NULL) {
225  ++devname;
226  } else {
227  devname = (char *) devpath;
228  }
229 
230  if (IOMasterPort(bootstrap_port, &port) != KERN_SUCCESS) {
231  fprintf(stderr, "IOMasterPort failed\n");
232  goto Exit;
233  }
234 
235  if (IOServiceGetMatchingServices(port, IOBSDNameMatching(port, 0, devname),
236  &iterator) != KERN_SUCCESS) {
237  fprintf(stderr, "IOServiceGetMatchingServices failed\n");
238  goto Exit;
239  }
240 
241  service = IOIteratorNext(iterator);
242 
243  IOObjectRelease(iterator);
244 
245  iterator = 0;
246 
247  while (service && !IOObjectConformsTo(service, "IOCDMedia")) {
248  if (IORegistryEntryGetParentIterator(service, kIOServicePlane,
249  &iterator) != KERN_SUCCESS)
250  {
251  fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
252  goto Exit;
253  }
254 
255  IOObjectRelease(service);
256  service = IOIteratorNext(iterator);
257  IOObjectRelease(iterator);
258  }
259 
260  if (!service) {
261  fprintf(stderr, "CD media not found\n");
262  goto Exit;
263  }
264 
265  if (IORegistryEntryCreateCFProperties(service, (__CFDictionary **) &properties,
266  kCFAllocatorDefault,
267  kNilOptions) != KERN_SUCCESS)
268  {
269  fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
270  goto Exit;
271  }
272 
273  data = (CFDataRef) CFDictionaryGetValue(properties, CFSTR(kIOCDMediaTOCKey));
274  if (data == NULL) {
275  fprintf(stderr, "CFDictionaryGetValue failed\n");
276  goto Exit;
277  } else {
278  CFRange range;
279  CFIndex buflen;
280 
281  buflen = CFDataGetLength(data) + 1;
282  range = CFRangeMake(0, buflen);
283  toc_p = (struct _CDTOC *) malloc(buflen);
284  if (toc_p == NULL) {
285  fprintf(stderr, "Out of memory\n");
286  goto Exit;
287  } else {
288  CFDataGetBytes(data, range, (unsigned char *) toc_p);
289  }
290 
291  /*
292  fprintf(stderr, "Table of contents\n length %d first %d last %d\n",
293  toc_p->length, toc_p->first_session, toc_p->last_session);
294  */
295 
296  CFRelease(properties);
297  }
298 
299  Exit:
300 
301  if (service) {
302  IOObjectRelease(service);
303  }
304 
305  return toc_p;
306 }
307 
308 #endif // defined(_CARBON_) || defined(__APPLE__)
309 
311 // AkaiSample:
312 AkaiSample::AkaiSample(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
313  : AkaiDiskElement(pDisk->GetPos())
314 {
315  mpParent = pParent;
316  mpDisk = pDisk;
317  mDirEntry = DirEntry;
318  mpSamples = NULL;
319  mHeaderOK = false;
320  mPos = 0;
321 
322  //
323  LoadHeader();
324 }
325 
326 AkaiSample::~AkaiSample()
327 {
328  if (mpSamples)
329  free(mpSamples);
330 }
331 
332 AkaiDirEntry AkaiSample::GetDirEntry()
333 {
334  return mDirEntry;
335 }
336 
337 bool AkaiSample::LoadSampleData()
338 {
339  if (!LoadHeader())
340  return false;
341  if (mpSamples)
342  return true;
343 
344  mpDisk->SetPos(mImageOffset);
345  mpSamples = (int16_t*) malloc(mNumberOfSamples * sizeof(int16_t));
346  if (!mpSamples)
347  return false;
348 
349  mpDisk->ReadInt16((uint16_t*)mpSamples, mNumberOfSamples);
350  return true;
351 }
352 
353 void AkaiSample::ReleaseSampleData()
354 {
355  if (!mpSamples)
356  return;
357  free(mpSamples);
358  mpSamples = NULL;
359 }
360 
361 int AkaiSample::SetPos(int Where, akai_stream_whence_t Whence)
362 {
363  if (!mHeaderOK) return -1;
364  int w = Where;
365  switch (Whence)
366  {
367  case akai_stream_start:
368  mPos = w;
369  break;
370  /*case eStreamRewind:
371  w = -w;*/
372  case akai_stream_curpos:
373  mPos += w;
374  break;
375  case akai_stream_end:
376  mPos = mNumberOfSamples - w;
377  break;
378  }
379  if (mPos > mNumberOfSamples) mPos = mNumberOfSamples;
380  if (mPos < 0) mPos = 0;
381  return mPos;
382 }
383 
384 int AkaiSample::Read(void* pBuffer, uint SampleCount)
385 {
386  if (!mHeaderOK) return 0;
387 
388  if (mPos + SampleCount > mNumberOfSamples) SampleCount = mNumberOfSamples - mPos;
389 
390  mpDisk->SetPos(mImageOffset + mPos * 2); // FIXME: assumes 16 bit sample depth
391  mpDisk->ReadInt16((uint16_t*)pBuffer, SampleCount);
392  return SampleCount;
393 }
394 
395 bool AkaiSample::LoadHeader()
396 {
397  if (mHeaderOK)
398  return true;
399 
400  mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
401 
402  // Length Format Description
403  // --------------------------------------------------------------
404  // 1 3
405  if (mpDisk->ReadInt8() != AKAI_SAMPLE_ID)
406  return false;
407  // 1 Not important: 0 for 22050Hz, 1 for 44100Hz
408  mpDisk->ReadInt8();
409  // 1 unsigned MIDI root note (C3=60)
410  mMidiRootNote = mpDisk->ReadInt8();
411  // 12 AKAII Filename
412  char buffer[13];
413  mpDisk->Read(buffer, 12, 1);
414  AkaiToAscii(buffer, 12);
415  mName = buffer;
416 
417  // 1 128
418  mpDisk->ReadInt8();
419  // 1 unsigned Number of active loops
420  mActiveLoops = mpDisk->ReadInt8();
421  // 1 unsigned char First active loop (0 for none)
422  mFirstActiveLoop = mpDisk->ReadInt8();
423  // 1 0
424  mpDisk->ReadInt8();
425  // 1 unsigned Loop mode: 0=in release 1=until release
426  // 2=none 3=play to end
427  mLoopMode = mpDisk->ReadInt8();
428  // 1 signed Cents tune -50...+50
429  mTuneCents = mpDisk->ReadInt8();
430  // 1 signed Semi tune -50...+50
431  mTuneSemitones = mpDisk->ReadInt8();
432  // 4 0,8,2,0
433  mpDisk->ReadInt8();
434  mpDisk->ReadInt8();
435  mpDisk->ReadInt8();
436  mpDisk->ReadInt8();
437  //
438  // 4 unsigned Number of sample words
439  mNumberOfSamples = mpDisk->ReadInt32();
440  // 4 unsigned Start marker
441  mStartMarker = mpDisk->ReadInt32();
442  // 4 unsigned End marker
443  mEndMarker = mpDisk->ReadInt32();
444  //
445  // 4 unsigned Loop 1 marker
446  // 2 unsigned Loop 1 fine length (65536ths)
447  // 4 unsigned Loop 1 coarse length (words)
448  // 2 unsigned Loop 1 time (msec. or 9999=infinite)
449  //
450  // 84 [as above] Loops 2 to 8
451  //
452  int i;
453  for (i=0; i<8; i++)
454  mLoops[i].Load(mpDisk);
455  // 4 0,0,255,255
456  mpDisk->ReadInt32();
457  // 2 unsigned Sampling frequency
458  mSamplingFrequency = mpDisk->ReadInt16();
459  // 1 signed char Loop tune offset -50...+50
460  mLoopTuneOffset = mpDisk->ReadInt8();
461 
462  mImageOffset = mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150; // 150 is the size of the header
463 
464  //FIXME: no header validation yet implemented
465  return (mHeaderOK = true);
466 }
467 
468 bool AkaiSampleLoop::Load(DiskImage* pDisk)
469 {
470  // 4 unsigned Loop 1 marker
471  mMarker = pDisk->ReadInt32();
472  // 2 unsigned Loop 1 fine length (65536ths)
473  mFineLength = pDisk->ReadInt16();
474  // 4 unsigned Loop 1 coarse length (words)
475  mCoarseLength = pDisk->ReadInt32();
476  // 2 unsigned Loop 1 time (msec. or 9999=infinite)
477  mTime = pDisk->ReadInt16();
478  return true;
479 }
480 
482 // AkaiProgram:
483 AkaiProgram::AkaiProgram(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
484  : AkaiDiskElement(pDisk->GetPos())
485 {
486  mpParent = pParent;
487  mpDisk = pDisk;
488  mDirEntry = DirEntry;
489  mpKeygroups = NULL;
490  Load();
491 }
492 
493 AkaiProgram::~AkaiProgram()
494 {
495  if (mpKeygroups)
496  delete[] mpKeygroups;
497 }
498 
499 AkaiDirEntry AkaiProgram::GetDirEntry()
500 {
501  return mDirEntry;
502 }
503 
504 bool AkaiProgram::Load()
505 {
506  // Read each of the program parameters
507  uint temppos = mpDisk->GetPos();
508  mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
509  // byte description default range/comments
510  // ---------------------------------------------------------------------------
511  // 1 program ID 1
512  uint8_t ProgID = mpDisk->ReadInt8();
513  if (ProgID != AKAI_PROGRAM_ID)
514  {
515  mpDisk->SetPos(temppos);
516  return false;
517  }
518  // 2-3 first keygroup address 150,0
519  /*uint16_t KeygroupAddress =*/ mpDisk->ReadInt16();
520  // 4-15 program name 10,10,10... AKAII character set
521  char buffer[13];
522  mpDisk->Read(buffer, 12, 1);
523  AkaiToAscii(buffer, 12);
524  mName = buffer;
525  // 16 MIDI program number 0 0..127
526  mMidiProgramNumber = mpDisk->ReadInt8();
527  // 17 MIDI channel 0 0..15, 255=OMNI
528  mMidiChannel = mpDisk->ReadInt8();
529  // 18 polyphony 15 1..16
530  mPolyphony = mpDisk->ReadInt8();
531  // 19 priority 1 0=LOW 1=NORM 2=HIGH 3=HOLD
532  mPriority = mpDisk->ReadInt8();
533  // 20 low key 24 24..127
534  mLowKey = mpDisk->ReadInt8();
535  // 21 high key 127 24..127
536  mHighKey = mpDisk->ReadInt8();
537  // 22 octave shift 0 -2..2
538  mOctaveShift = mpDisk->ReadInt8();
539  // 23 aux output select 255 0..7, 255=OFF
540  mAuxOutputSelect = mpDisk->ReadInt8();
541  // 24 mix output level 99 0..99
542  mMixOutputSelect = mpDisk->ReadInt8();
543  // 25 mix output pan 0 -50..50
544  mMixPan = mpDisk->ReadInt8();
545  // 26 volume 80 0..99
546  mVolume = mpDisk->ReadInt8();
547  // 27 vel>volume 20 -50..50
548  mVelocityToVolume = mpDisk->ReadInt8();
549  // 28 key>volume 0 -50..50
550  mKeyToVolume = mpDisk->ReadInt8();
551  // 29 pres>volume 0 -50..50
552  mPressureToVolume = mpDisk->ReadInt8();
553  // 30 pan lfo rate 50 0..99
554  mPanLFORate = mpDisk->ReadInt8();
555  // 31 pan lfo depth 0 0..99
556  mPanLFODepth = mpDisk->ReadInt8();
557  // 32 pan lfo delay 0 0..99
558  mPanLFODelay = mpDisk->ReadInt8();
559  // 33 key>pan 0 -50..50
560  mKeyToPan = mpDisk->ReadInt8();
561  // 34 lfo rate 50 0..99
562  mLFORate = mpDisk->ReadInt8();
563  // 35 lfo depth 0 0..99
564  mLFODepth = mpDisk->ReadInt8();
565  // 36 lfo delay 0 0..99
566  mLFODelay = mpDisk->ReadInt8();
567  // 37 mod>lfo depth 30 0..99
568  mModulationToLFODepth = mpDisk->ReadInt8();
569  // 38 pres>lfo depth 0 0..99
570  mPressureToLFODepth = mpDisk->ReadInt8();
571  // 39 vel>lfo depth 0 0..99
572  mVelocityToLFODepth = mpDisk->ReadInt8();
573  // 40 bend>pitch 2 0..12 semitones
574  mBendToPitch = mpDisk->ReadInt8();
575  // 41 pres>pitch 0 -12..12 semitones
576  mPressureToPitch = mpDisk->ReadInt8();
577  // 42 keygroup crossfade 0 0=OFF 1=ON
578  mKeygroupCrossfade = mpDisk->ReadInt8()?true:false;
579  // 43 number of keygroups 1 1..99
580  mNumberOfKeygroups = mpDisk->ReadInt8();
581  // 44 (internal use) 0 program number
582  mpDisk->ReadInt8();
583  // 45-56 key temperament C,C#,D... 0 -25..25 cents
584  uint i;
585  for (i = 0; i<11; i++)
586  mKeyTemperament[i] = mpDisk->ReadInt8();
587  // 57 fx output 0 0=OFF 1=ON
588  mFXOutput = mpDisk->ReadInt8()?true:false;
589  // 58 mod>pan 0 -50..50
590  mModulationToPan = mpDisk->ReadInt8();
591  // 59 stereo coherence 0 0=OFF 1=ON
592  mStereoCoherence = mpDisk->ReadInt8()?true:false;
593  // 60 lfo desync 1 0=OFF 1=ON
594  mLFODesync = mpDisk->ReadInt8()?true:false;
595  // 61 pitch law 0 0=LINEAR
596  mPitchLaw = mpDisk->ReadInt8();
597  // 62 voice re-assign 0 0=OLDEST 1=QUIETEST
598  mVoiceReassign = mpDisk->ReadInt8();
599  // 63 softped>volume 10 0..99
600  mSoftpedToVolume = mpDisk->ReadInt8();
601  // 64 softped>attack 10 0..99
602  mSoftpedToAttack = mpDisk->ReadInt8();
603  // 65 softped>filt 10 0..99
604  mSoftpedToFilter = mpDisk->ReadInt8();
605  // 66 tune cents 0 -128..127 (-50..50 cents)
606  mSoftpedToTuneCents = mpDisk->ReadInt8();
607  // 67 tune semitones 0 -50..50
608  mSoftpedToTuneSemitones = mpDisk->ReadInt8();
609  // 68 key>lfo rate 0 -50..50
610  mKeyToLFORate = mpDisk->ReadInt8();
611  // 69 key>lfo depth 0 -50..50
612  mKeyToLFODepth = mpDisk->ReadInt8();
613  // 70 key>lfo delay 0 -50..50
614  mKeyToLFODelay = mpDisk->ReadInt8();
615  // 71 voice output scale 1 0=-6dB 1=0dB 2=+12dB
616  mVoiceOutputScale = mpDisk->ReadInt8();
617  // 72 stereo output scale 0 0=0dB 1=+6dB
618  mStereoOutputScale = mpDisk->ReadInt8();
619  // 73-150 (not used)
620 
621  // Read the key groups:
622  if (mpKeygroups)
623  delete[] mpKeygroups;
624  mpKeygroups = new AkaiKeygroup[mNumberOfKeygroups];
625  for (i = 0; i < mNumberOfKeygroups; i++)
626  {
627  // Go where the key group is on the disk:
628  mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150 * (i+1));
629  if (!mpKeygroups[i].Load(mpDisk))
630  {
631  mpDisk->SetPos(temppos);
632  return false;
633  }
634  }
635 
636  mpDisk->SetPos(temppos);
637  return true;
638 }
639 
640 uint AkaiProgram::ListSamples(std::list<String>& rSamples)
641 {
642  return 0;
643 }
644 
645 AkaiSample* AkaiProgram::GetSample(uint Index)
646 {
647  return NULL;
648 }
649 
650 AkaiSample* AkaiProgram::GetSample(const String& rName)
651 {
652  return NULL;
653 }
654 
655 // AkaiKeygroup:
656 bool AkaiKeygroup::Load(DiskImage* pDisk)
657 {
658  uint i;
659  // byte description default range/comments
660  // ---------------------------------------------------------------------------
661  // 1 keygroup ID 2
662  if (pDisk->ReadInt8() != AKAI_KEYGROUP_ID)
663  return false;
664  // 2-3 next keygroup address 44,1 300,450,600,750.. (16-bit)
665  /*uint16_t NextKeygroupAddress =*/ pDisk->ReadInt16();
666  // 4 low key 24 24..127
667  mLowKey = pDisk->ReadInt8();
668  // 5 high key 127 24..127
669  mHighKey = pDisk->ReadInt8();
670  // 6 tune cents 0 -128..127 (-50..50 cents)
671  mTuneCents = pDisk->ReadInt8();
672  // 7 tune semitones 0 -50..50
673  mTuneSemitones = pDisk->ReadInt8();
674  // 8 filter 99 0..99
675  mFilter = pDisk->ReadInt8();
676  // 9 key>filter 12 0..24 semitone/oct
677  mKeyToFilter = pDisk->ReadInt8();
678  // 10 vel>filt 0 -50..50
679  mVelocityToFilter = pDisk->ReadInt8();
680  // 11 pres>filt 0 -50..50
681  mPressureToFilter = pDisk->ReadInt8();
682  // 12 env2>filt 0 -50..50
683  mEnveloppe2ToFilter = pDisk->ReadInt8();
684 
685  // 13 env1 attack 0 0..99
686  // 14 env1 decay 30 0..99
687  // 15 env1 sustain 99 0..99
688  // 16 env1 release 45 0..99
689  // 17 env1 vel>attack 0 -50..50
690  // 18 env1 vel>release 0 -50..50
691  // 19 env1 offvel>release 0 -50..50
692  // 20 env1 key>dec&rel 0 -50..50
693  // 21 env2 attack 0 0..99
694  // 22 env2 decay 50 0..99
695  // 23 env2 sustain 99 0..99
696  // 24 env2 release 45 0..99
697  // 25 env2 vel>attack 0 -50..50
698  // 26 env2 vel>release 0 -50..50
699  // 27 env2 offvel>release 0 -50..50
700  // 28 env2 key>dec&rel 0 -50..50
701  for (i=0; i<2; i++)
702  mEnveloppes[i].Load(pDisk);
703 
704  // 29 vel>env2>filter 0 -50..50
705  mVelocityToEnveloppe2ToFilter = pDisk->ReadInt8();
706  // 30 env2>pitch 0 -50..50
707  mEnveloppe2ToPitch = pDisk->ReadInt8();
708  // 31 vel zone crossfade 1 0=OFF 1=ON
709  mVelocityZoneCrossfade = pDisk->ReadInt8()?true:false;
710  // 32 vel zones used 4
711  mVelocityZoneUsed = pDisk->ReadInt8();
712  // 33 (internal use) 255
713  pDisk->ReadInt8();
714  // 34 (internal use) 255
715  pDisk->ReadInt8();
716  //
717 
718  // 35-46 sample 1 name 10,10,10... AKAII character set
719  // 47 low vel 0 0..127
720  // 48 high vel 127 0..127
721  // 49 tune cents 0 -128..127 (-50..50 cents)
722  // 50 tune semitones 0 -50..50
723  // 51 loudness 0 -50..+50
724  // 52 filter 0 -50..+50
725  // 53 pan 0 -50..+50
726  // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
727  // 2=LOOP_UNTIL_REL 3=NO_LOOP
728  // 4=PLAY_TO_END
729  // 55 (internal use) 255
730  // 56 (internal use) 255
731  // 57-58 (internal use) 44,1
732  //
733  // 59-82 [repeat 35-58 for sample 2]
734  //
735  // 83-106 [repeat 35-58 for sample 3]
736  //
737  // 107-130 [repeat 35-58 for sample 4]
738  //
739  for (i=0; i<4; i++)
740  mSamples[i].Load(pDisk);
741 
742  // 131 beat detune 0 -50..50
743  mBeatDetune = pDisk->ReadInt8();
744  // 132 hold attack until loop 0 0=OFF 1=ON
745  mHoldAttackUntilLoop = pDisk->ReadInt8()?true:false;
746  // 133-136 sample 1-4 key tracking 0 0=TRACK 1=FIXED
747  for (i=0; i<4; i++)
748  mSampleKeyTracking[i] = pDisk->ReadInt8()?true:false;
749  // 137-140 sample 1-4 aux out offset 0 0..7
750  for (i=0; i<4; i++)
751  mSampleAuxOutOffset[i] = pDisk->ReadInt8();
752  // 141-148 vel>sample start 0 -9999..9999 (16-bit signed)
753  for (i=0; i<4; i++)
754  mVelocityToSampleStart[i] = pDisk->ReadInt8();
755  // 149 vel>volume offset 0 -50..50
756  for (i=0; i<4; i++)
757  mVelocityToVolumeOffset[i] = pDisk->ReadInt8();
758  // 150 (not used)
759 
760  return true;
761 }
762 
763 bool AkaiEnveloppe::Load(DiskImage* pDisk)
764 {
765  // 13 env1 attack 0 0..99
766  mAttack = pDisk->ReadInt8();
767  // 14 env1 decay 30 0..99
768  mDecay = pDisk->ReadInt8();
769  // 15 env1 sustain 99 0..99
770  mSustain = pDisk->ReadInt8();
771  // 16 env1 release 45 0..99
772  mRelease = pDisk->ReadInt8();
773  // 17 env1 vel>attack 0 -50..50
774  mVelocityToAttack = pDisk->ReadInt8();
775  // 18 env1 vel>release 0 -50..50
776  mVelocityToRelease = pDisk->ReadInt8();
777  // 19 env1 offvel>release 0 -50..50
778  mOffVelocityToRelease = pDisk->ReadInt8();
779  // 20 env1 key>dec&rel 0 -50..50
780  mKeyToDecayAndRelease = pDisk->ReadInt8();
781  return true;
782 }
783 
784 bool AkaiKeygroupSample::Load(DiskImage* pDisk)
785 {
786  // 35-46 sample 1 name 10,10,10... AKAII character set
787  char buffer[13];
788  pDisk->Read(buffer, 12, 1);
789  AkaiToAscii(buffer, 12);
790  mName = buffer;
791 
792  // 47 low vel 0 0..127
793  mLowLevel = pDisk->ReadInt8();
794  // 48 high vel 127 0..127
795  /*uint8_t mHighLevel =*/ pDisk->ReadInt8();
796  // 49 tune cents 0 -128..127 (-50..50 cents)
797  /*int8_t mTuneCents =*/ pDisk->ReadInt8();
798  // 50 tune semitones 0 -50..50
799  /*int8_t mTuneSemitones =*/ pDisk->ReadInt8();
800  // 51 loudness 0 -50..+50
801  /*int8_t mLoudness =*/ pDisk->ReadInt8();
802  // 52 filter 0 -50..+50
803  /*int8_t mFilter =*/ pDisk->ReadInt8();
804  // 53 pan 0 -50..+50
805  /*int8_t mPan =*/ pDisk->ReadInt8();
806  // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
807  // 2=LOOP_UNTIL_REL 3=NO_LOOP
808  // 4=PLAY_TO_END
809  /*uint8_t mLoopMode =*/ pDisk->ReadInt8();
810  // 55 (internal use) 255
811  pDisk->ReadInt8();
812  // 56 (internal use) 255
813  pDisk->ReadInt8();
814  // 57-58 (internal use) 44,1
815  pDisk->ReadInt16();
816  //
817 
818  return true;
819 }
820 
822 // AkaiVolume:
823 AkaiVolume::AkaiVolume(DiskImage* pDisk, AkaiPartition* pParent, const AkaiDirEntry& DirEntry)
824  : AkaiDiskElement()
825 {
826  mpDisk = pDisk;
827  mpParent = pParent;
828  mDirEntry = DirEntry;
829 
830  if (mDirEntry.mType != AKAI_TYPE_DIR_S1000 && mDirEntry.mType != AKAI_TYPE_DIR_S3000)
831  {
832  printf("Creating Unknown Volume type! %d\n",mDirEntry.mType);
833 #ifdef WIN32
834  OutputDebugString("Creating Unknown Volume type!\n");
835 #endif
836  }
837 }
838 
839 AkaiVolume::~AkaiVolume()
840 {
841  {
842  std::list<AkaiProgram*>::iterator it;
843  std::list<AkaiProgram*>::iterator end = mpPrograms.end();
844  for (it = mpPrograms.begin(); it != end; it++)
845  if (*it)
846  (*it)->Release();
847  }
848 
849  {
850  std::list<AkaiSample*>::iterator it;
851  std::list<AkaiSample*>::iterator end = mpSamples.end();
852  for (it = mpSamples.begin(); it != end; it++)
853  if (*it)
854  (*it)->Release();
855  }
856 }
857 
858 uint AkaiVolume::ReadDir()
859 {
860  uint i;
861  if (mpPrograms.empty())
862  {
863  uint maxfiles = ReadFAT(mpDisk, mpParent,mDirEntry.mStart) ? AKAI_MAX_FILE_ENTRIES_S1000 : AKAI_MAX_FILE_ENTRIES_S3000;
864  for (i = 0; i < maxfiles; i++)
865  {
866  AkaiDirEntry DirEntry;
867  ReadDirEntry(mpDisk, mpParent, DirEntry, mDirEntry.mStart, i);
868  DirEntry.mIndex = i;
869  if (DirEntry.mType == 'p')
870  {
871  AkaiProgram* pProgram = new AkaiProgram(mpDisk, this, DirEntry);
872  pProgram->Acquire();
873  mpPrograms.push_back(pProgram);
874  }
875  else if (DirEntry.mType == 's')
876  {
877  AkaiSample* pSample = new AkaiSample(mpDisk, this, DirEntry);
878  pSample->Acquire();
879  mpSamples.push_back(pSample);
880  }
881  }
882  }
883  return (uint)(mpPrograms.size() + mpSamples.size());
884 }
885 
886 uint AkaiVolume::ListPrograms(std::list<AkaiDirEntry>& rPrograms)
887 {
888  rPrograms.clear();
889  ReadDir();
890 
891  std::list<AkaiProgram*>::iterator it;
892  std::list<AkaiProgram*>::iterator end = mpPrograms.end();
893  for (it = mpPrograms.begin(); it != end; it++)
894  if (*it)
895  rPrograms.push_back((*it)->GetDirEntry());
896  return (uint)rPrograms.size();
897 }
898 
899 AkaiProgram* AkaiVolume::GetProgram(uint Index)
900 {
901  uint i = 0;
902 
903  if (mpPrograms.empty())
904  {
905  std::list<AkaiDirEntry> dummy;
906  ListPrograms(dummy);
907  }
908 
909  std::list<AkaiProgram*>::iterator it;
910  std::list<AkaiProgram*>::iterator end = mpPrograms.end();
911  for (it = mpPrograms.begin(); it != end; it++)
912  {
913  if (*it && i == Index)
914  {
915  (*it)->Acquire();
916  return *it;
917  }
918  i++;
919  }
920  return NULL;
921 }
922 
923 AkaiProgram* AkaiVolume::GetProgram(const String& rName)
924 {
925  if (mpPrograms.empty())
926  {
927  std::list<AkaiDirEntry> dummy;
928  ListPrograms(dummy);
929  }
930 
931  std::list<AkaiProgram*>::iterator it;
932  std::list<AkaiProgram*>::iterator end = mpPrograms.end();
933  for (it = mpPrograms.begin(); it != end; it++)
934  {
935  if (*it && rName == (*it)->GetDirEntry().mName)
936  {
937  (*it)->Acquire();
938  return *it;
939  }
940  }
941  return NULL;
942 }
943 
944 uint AkaiVolume::ListSamples(std::list<AkaiDirEntry>& rSamples)
945 {
946  rSamples.clear();
947  ReadDir();
948 
949  std::list<AkaiSample*>::iterator it;
950  std::list<AkaiSample*>::iterator end = mpSamples.end();
951  for (it = mpSamples.begin(); it != end; it++)
952  if (*it)
953  rSamples.push_back((*it)->GetDirEntry());
954  return (uint)rSamples.size();
955 }
956 
957 AkaiSample* AkaiVolume::GetSample(uint Index)
958 {
959  uint i = 0;
960 
961  if (mpSamples.empty())
962  {
963  std::list<AkaiDirEntry> dummy;
964  ListSamples(dummy);
965  }
966 
967  std::list<AkaiSample*>::iterator it;
968  std::list<AkaiSample*>::iterator end = mpSamples.end();
969  for (it = mpSamples.begin(); it != end; it++)
970  {
971  if (*it && i == Index)
972  {
973  (*it)->Acquire();
974  return *it;
975  }
976  i++;
977  }
978  return NULL;
979 }
980 
981 AkaiSample* AkaiVolume::GetSample(const String& rName)
982 {
983  if (mpSamples.empty())
984  {
985  std::list<AkaiDirEntry> dummy;
986  ListSamples(dummy);
987  }
988 
989  std::list<AkaiSample*>::iterator it;
990  std::list<AkaiSample*>::iterator end = mpSamples.end();
991  for (it = mpSamples.begin(); it != end; it++)
992  {
993  if (*it && rName == (*it)->GetDirEntry().mName)
994  {
995  (*it)->Acquire();
996  return *it;
997  }
998  }
999  return NULL;
1000 }
1001 
1002 AkaiDirEntry AkaiVolume::GetDirEntry()
1003 {
1004  return mDirEntry;
1005 }
1006 
1007 bool AkaiVolume::IsEmpty()
1008 {
1009  return ReadDir() == 0;
1010 }
1011 
1012 
1014 // AkaiPartition:
1015 AkaiPartition::AkaiPartition(DiskImage* pDisk, AkaiDisk* pParent)
1016 {
1017  mpDisk = pDisk;
1018  mpParent = pParent;
1019 }
1020 
1021 AkaiPartition::~AkaiPartition()
1022 {
1023  std::list<AkaiVolume*>::iterator it;
1024  std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1025  for (it = mpVolumes.begin(); it != end; it++)
1026  if (*it)
1027  (*it)->Release();
1028 }
1029 
1030 uint AkaiPartition::ListVolumes(std::list<AkaiDirEntry>& rVolumes)
1031 {
1032  rVolumes.clear();
1033  uint i;
1034  if (mpVolumes.empty())
1035  {
1036  for (i = 0; i < AKAI_MAX_DIR_ENTRIES; i++)
1037  {
1038  AkaiDirEntry DirEntry;
1039  ReadDirEntry(mpDisk, this, DirEntry, AKAI_ROOT_ENTRY_OFFSET, i);
1040  DirEntry.mIndex = i;
1041  if (DirEntry.mType == AKAI_TYPE_DIR_S1000 || DirEntry.mType == AKAI_TYPE_DIR_S3000)
1042  {
1043  AkaiVolume* pVolume = new AkaiVolume(mpDisk, this, DirEntry);
1044  pVolume->Acquire();
1045  if (!pVolume->IsEmpty())
1046  {
1047  mpVolumes.push_back(pVolume);
1048  rVolumes.push_back(DirEntry);
1049  }
1050  else
1051  pVolume->Release();
1052  }
1053  }
1054  }
1055  else
1056  {
1057  std::list<AkaiVolume*>::iterator it;
1058  std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1059  for (it = mpVolumes.begin(); it != end; it++)
1060  if (*it)
1061  rVolumes.push_back((*it)->GetDirEntry());
1062  }
1063  return (uint)rVolumes.size();
1064 }
1065 
1066 AkaiVolume* AkaiPartition::GetVolume(uint Index)
1067 {
1068  uint i = 0;
1069 
1070  if (mpVolumes.empty())
1071  {
1072  std::list<AkaiDirEntry> dummy;
1073  ListVolumes(dummy);
1074  }
1075 
1076  std::list<AkaiVolume*>::iterator it;
1077  std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1078  for (it = mpVolumes.begin(); it != end; it++)
1079  {
1080  if (*it && i == Index)
1081  {
1082  (*it)->Acquire();
1083  return *it;
1084  }
1085  i++;
1086  }
1087  return NULL;
1088 }
1089 
1090 AkaiVolume* AkaiPartition::GetVolume(const String& rName)
1091 {
1092  if (mpVolumes.empty())
1093  {
1094  std::list<AkaiDirEntry> dummy;
1095  ListVolumes(dummy);
1096  }
1097 
1098  std::list<AkaiVolume*>::iterator it;
1099  std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1100  for (it = mpVolumes.begin(); it != end; it++)
1101  {
1102  if (*it && rName == (*it)->GetDirEntry().mName)
1103  {
1104  (*it)->Acquire();
1105  return *it;
1106  }
1107  }
1108  return NULL;
1109 }
1110 
1111 bool AkaiPartition::IsEmpty()
1112 {
1113  std::list<AkaiDirEntry> Volumes;
1114  return ListVolumes(Volumes) == 0;
1115 }
1116 
1117 
1119 // AkaiDisk:
1120 AkaiDisk::AkaiDisk(DiskImage* pDisk)
1121 {
1122  mpDisk = pDisk;
1123 }
1124 
1125 AkaiDisk::~AkaiDisk()
1126 {
1127  std::list<AkaiPartition*>::iterator it;
1128  std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1129  for (it = mpPartitions.begin(); it != end ; it++)
1130  if (*it)
1131  (*it)->Release();
1132 }
1133 
1134 uint AkaiDisk::GetPartitionCount()
1135 {
1136  if (!mpPartitions.empty())
1137  return (uint)mpPartitions.size();
1138 
1139  uint offset = 0;
1140  uint16_t size = 0;
1141  while (size != AKAI_PARTITION_END_MARK && size != 0x0fff && size != 0xffff
1142  && size<30720 && mpPartitions.size()<9)
1143  {
1144  // printf("size: %x\t",size);
1145  AkaiPartition* pPartition = new AkaiPartition(mpDisk,this);
1146  pPartition->Acquire();
1147  pPartition->SetOffset(offset);
1148 
1149  if (!pPartition->IsEmpty())
1150  {
1151  mpPartitions.push_back(pPartition); // Add this partitions' offset to the list.
1152  }
1153 
1154  mpDisk->SetPos(offset);
1155  if (!mpDisk->ReadInt16(&size))
1156  return (uint)mpPartitions.size();
1157  uint t = size;
1158  offset += AKAI_BLOCK_SIZE * t;
1159 // printf("new offset %d / size %d\n",offset,size);
1160  }
1161 
1162  return (uint)mpPartitions.size();
1163 }
1164 
1165 AkaiPartition* AkaiDisk::GetPartition(uint count)
1166 {
1167  std::list<AkaiPartition*>::iterator it;
1168  std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1169  uint i = 0;
1170  for (i = 0, it = mpPartitions.begin(); it != end && i != count; i++) it++;
1171 
1172  if (i != count || it == end)
1173  return NULL;
1174 
1175  (*it)->Acquire();
1176  return *it;
1177 }
1178 
1180 // AkaiDiskElement
1181 
1182 int AkaiDiskElement::ReadFAT(DiskImage* pDisk, AkaiPartition* pPartition, int block)
1183 {
1184  int16_t value = 0;
1185  pDisk->SetPos(pPartition->GetOffset()+AKAI_FAT_OFFSET + block*2);
1186  pDisk->Read(&value, 2,1);
1187  return value;
1188 }
1189 
1190 
1191 bool AkaiDiskElement::ReadDirEntry(DiskImage* pDisk, AkaiPartition* pPartition, AkaiDirEntry& rEntry, int block, int pos)
1192 {
1193  char buffer[13];
1194 
1195  if (block == AKAI_ROOT_ENTRY_OFFSET)
1196  {
1197  pDisk->SetPos(pPartition->GetOffset()+AKAI_DIR_ENTRY_OFFSET + pos * AKAI_DIR_ENTRY_SIZE);
1198  pDisk->Read(buffer, 12, 1);
1199  AkaiToAscii(buffer, 12);
1200  rEntry.mName = buffer;
1201 
1202  pDisk->ReadInt16(&rEntry.mType);
1203  pDisk->ReadInt16(&rEntry.mStart);
1204  rEntry.mSize = 0;
1205  return true;
1206  }
1207  else
1208  {
1209  if (pos < 341)
1210  {
1211  pDisk->SetPos(block * AKAI_BLOCK_SIZE + pos * AKAI_FILE_ENTRY_SIZE + pPartition->GetOffset());
1212  }
1213  else
1214  {
1215  int temp;
1216  temp = ReadFAT(pDisk, pPartition, block);
1217  pDisk->SetPos(pPartition->GetOffset()+temp * AKAI_BLOCK_SIZE + (pos - 341) * AKAI_FILE_ENTRY_SIZE);
1218  }
1219 
1220  pDisk->Read(buffer, 12, 1);
1221  AkaiToAscii(buffer, 12);
1222  rEntry.mName = buffer;
1223 
1224  uint8_t t1,t2,t3;
1225  pDisk->SetPos(4,akai_stream_curpos);
1226  pDisk->Read(&t1, 1,1);
1227  rEntry.mType = t1;
1228 
1229  pDisk->Read(&t1, 1,1);
1230  pDisk->Read(&t2, 1,1);
1231  pDisk->Read(&t3, 1,1);
1232  rEntry.mSize = (unsigned char)t1 | ((unsigned char)t2 <<8) | ((unsigned char)t3<<16);
1233 
1234  pDisk->ReadInt16(&rEntry.mStart,1);
1235  return true;
1236  } // not root block
1237 }
1238 
1239 void AkaiDiskElement::AkaiToAscii(char * buffer, int length)
1240 {
1241  int i;
1242 
1243  for (i = 0; i < length; i++)
1244  {
1245  if (buffer[i]>=0 && buffer[i]<=9)
1246  buffer[i] +=48;
1247  else if (buffer[i]==10)
1248  buffer[i] = 32;
1249  else if (buffer[i]>=11 && buffer[i]<=36)
1250  buffer[i] = 64+(buffer[i]-10);
1251  else
1252  buffer[i] = 32;
1253  }
1254  buffer[length] = '\0';
1255  while (length-- > 0 && buffer[length] == 32)
1256  {
1257  // This block intentionaly left blank :)
1258  }
1259  buffer[length+1] = '\0';
1260 }
1261 
1262 
1264 // DiskImage:
1265 
1266 DiskImage::DiskImage(const char* path)
1267 {
1268  Init();
1269  OpenStream(path);
1270 }
1271 
1272 #ifdef _CARBON_
1273 kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
1274 {
1275  kern_return_t kernResult;
1276  mach_port_t masterPort;
1277  CFMutableDictionaryRef classesToMatch;
1278 
1279  kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
1280  if ( KERN_SUCCESS != kernResult )
1281  {
1282  printf( "IOMasterPort returned %d\n", kernResult );
1283  }
1284 
1285  classesToMatch = IOServiceMatching( kIOCDMediaClass );
1286  if ( classesToMatch == NULL )
1287  {
1288  printf( "IOServiceMatching returned a NULL dictionary.\n" );
1289  }
1290  else
1291  {
1292  CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
1293  }
1294 
1295  kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
1296  if ( KERN_SUCCESS != kernResult )
1297  {
1298  printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
1299  }
1300 
1301  return kernResult;
1302 }
1303 
1304 kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
1305 {
1306  io_object_t nextMedia;
1307  kern_return_t kernResult = KERN_FAILURE;
1308 
1309  *bsdPath = '\0';
1310 
1311  nextMedia = IOIteratorNext( mediaIterator );
1312  if ( nextMedia )
1313  {
1314  CFTypeRef bsdPathAsCFString;
1315 
1316  bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia,
1317  CFSTR( kIOBSDNameKey ),
1318  kCFAllocatorDefault,
1319  0 );
1320  if ( bsdPathAsCFString )
1321  {
1322  size_t devPathLength;
1323 
1324  strcpy( bsdPath, _PATH_DEV );
1325  strcat( bsdPath, "r" );
1326 
1327  devPathLength = strlen( bsdPath );
1328 
1329  if ( CFStringGetCString( (__CFString*)bsdPathAsCFString,
1330  bsdPath + devPathLength,
1331  maxPathSize - devPathLength,
1332  kCFStringEncodingASCII ) )
1333  {
1334  printf( "BSD path: %s\n", bsdPath );
1335  kernResult = KERN_SUCCESS;
1336  }
1337 
1338  CFRelease( bsdPathAsCFString );
1339  }
1340  IOObjectRelease( nextMedia );
1341  }
1342 
1343  return kernResult;
1344 }
1345 
1346 struct _CDMSF
1347 {
1348  u_char minute;
1349  u_char second;
1350  u_char frame;
1351 };
1352 
1353 /* converting from minute-second to logical block entity */
1354 #define MSF_TO_LBA(msf) (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
1355 
1356 struct _CDTOC_Desc
1357 {
1358  u_char session;
1359  u_char ctrl_adr; /* typed to be machine and compiler independent */
1360  u_char tno;
1361  u_char point;
1362  struct _CDMSF address;
1363  u_char zero;
1364  struct _CDMSF p;
1365 };
1366 
1367 struct _CDTOC
1368 {
1369  u_short length; /* in native cpu endian */
1370  u_char first_session;
1371  u_char last_session;
1372  struct _CDTOC_Desc trackdesc[1];
1373 };
1374 
1375 static struct _CDTOC * ReadTOC (const char * devpath )
1376 {
1377  struct _CDTOC * toc_p = NULL;
1378  io_iterator_t iterator = 0;
1379  io_registry_entry_t service = 0;
1380  CFDictionaryRef properties = 0;
1381  CFDataRef data = 0;
1382  mach_port_t port = 0;
1383  char * devname;
1384  if (( devname = strrchr( devpath, '/' )) != NULL )
1385  {
1386  ++devname;
1387  }
1388  else
1389  {
1390  devname = ( char *) devpath;
1391  }
1392 
1393  if ( IOMasterPort(bootstrap_port, &port ) != KERN_SUCCESS )
1394  {
1395  printf( "IOMasterPort failed\n" ); goto Exit ;
1396  }
1397  if ( IOServiceGetMatchingServices( port, IOBSDNameMatching( port, 0, devname ),
1398  &iterator ) != KERN_SUCCESS )
1399  {
1400  printf( "IOServiceGetMatchingServices failed\n" ); goto Exit ;
1401  }
1402 
1403  char buffer[1024];
1404  IOObjectGetClass(iterator,buffer);
1405  printf("Service: %s\n",buffer);
1406 
1407 
1408  IOIteratorReset (iterator);
1409  service = IOIteratorNext( iterator );
1410 
1411  IOObjectRelease( iterator );
1412 
1413  iterator = 0;
1414  while ( service && !IOObjectConformsTo( service, "IOCDMedia" ))
1415  {
1416  char buffer[1024];
1417  IOObjectGetClass(service,buffer);
1418  printf("Service: %s\n",buffer);
1419 
1420  if ( IORegistryEntryGetParentIterator( service, kIOServicePlane, &iterator ) != KERN_SUCCESS )
1421  {
1422  printf( "IORegistryEntryGetParentIterator failed\n" );
1423  goto Exit ;
1424  }
1425 
1426  IOObjectRelease( service );
1427  service = IOIteratorNext( iterator );
1428  IOObjectRelease( iterator );
1429 
1430  }
1431  if ( service == NULL )
1432  {
1433  printf( "CD media not found\n" ); goto Exit ;
1434  }
1435  if ( IORegistryEntryCreateCFProperties( service, (__CFDictionary **) &properties,
1436  kCFAllocatorDefault,
1437  kNilOptions ) != KERN_SUCCESS )
1438  {
1439  printf( "IORegistryEntryGetParentIterator failed\n" ); goto Exit ;
1440  }
1441 
1442  data = (CFDataRef) CFDictionaryGetValue( properties, CFSTR( "TOC" ) );
1443  if ( data == NULL )
1444  {
1445  printf( "CFDictionaryGetValue failed\n" ); goto Exit ;
1446  }
1447  else
1448  {
1449 
1450  CFRange range;
1451  CFIndex buflen;
1452 
1453  buflen = CFDataGetLength( data ) + 1;
1454  range = CFRangeMake( 0, buflen );
1455  toc_p = ( struct _CDTOC *) malloc( buflen );
1456  if ( toc_p == NULL )
1457  {
1458  printf( "Out of memory\n" ); goto Exit ;
1459  }
1460  else
1461  {
1462  CFDataGetBytes( data, range, ( unsigned char *) toc_p );
1463  }
1464 
1465  /*
1466  fprintf( stderr, "Table of contents\n length %d first %d last %d\n",
1467  toc_p->length, toc_p->first_session, toc_p->last_session );
1468  */
1469 
1470  CFRelease( properties );
1471 
1472  }
1473 Exit :
1474  if ( service )
1475  {
1476  IOObjectRelease( service );
1477  }
1478 
1479  return toc_p;
1480 }
1481 #endif // _CARBON_
1482 
1484 {
1485  Init();
1486 #ifdef _WIN32_
1487  char buffer[1024];
1488  sprintf(buffer,"%c:\\",'A'+disk);
1489  mSize = GetFileSize(buffer,NULL);
1490 
1491  sprintf(buffer,"\\\\.\\%c:",'a'+disk);
1492  mFile = CreateFile(buffer, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1493 
1494  DWORD junk;
1495  DISK_GEOMETRY dg;
1496  DISK_GEOMETRY* pdg = &dg;
1497  BOOL res = DeviceIoControl(mFile,
1498  IOCTL_DISK_GET_DRIVE_GEOMETRY,
1499  NULL, 0,
1500  pdg, sizeof(*pdg),
1501  &junk,
1502  NULL);
1503 
1504  if (res)
1505  {
1506  mSize = dg.BytesPerSector * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.Cylinders.LowPart;
1507  mClusterSize = dg.BytesPerSector;
1508  }
1509 #elif defined(_CARBON_) || defined(__APPLE__)
1510  kern_return_t kernResult;
1511  io_iterator_t mediaIterator;
1512  char bsdPath[ MAXPATHLEN ];
1513  kernResult = FindEjectableCDMedia( &mediaIterator );
1514  kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
1515  if ( bsdPath[ 0 ] != '\0' )
1516  {
1517  strcpy(bsdPath,"/dev/rdisk1s0");
1518 
1519  struct _CDTOC * toc = ReadTOC( bsdPath );
1520  if ( toc )
1521  {
1522  size_t toc_entries = ( toc->length - 2 ) / sizeof (struct _CDTOC_Desc );
1523 
1524  int start_sector = -1;
1525  int data_track = -1;
1526  // Iterate through the list backward. Pick the first data track and
1527  // get the address of the immediately previous (or following depending
1528  // on how you look at it). The difference in the sector numbers
1529  // is returned as the sized of the data track.
1530  for (ssize_t i=toc_entries - 1; i>=0; i-- )
1531  {
1532  if ( start_sector != -1 )
1533  {
1534  start_sector = MSF_TO_LBA(toc->trackdesc[i].p) - start_sector;
1535  break ;
1536 
1537  }
1538  if (( toc->trackdesc[i].ctrl_adr >> 4) != 1 )
1539  continue ;
1540  if ( toc->trackdesc[i].ctrl_adr & 0x04 )
1541  {
1542  data_track = toc->trackdesc[i].point;
1543  start_sector = MSF_TO_LBA(toc->trackdesc[i].p);
1544  }
1545  }
1546 
1547  free( toc );
1548  if ( start_sector == -1 )
1549  {
1550  start_sector = 0;
1551  }
1552  }
1553 // mSize = start_sector;
1554 // printf("size %d\n",mSize);
1555 
1556 
1557  mFile = open(bsdPath,O_RDONLY);
1558  if (!mFile)
1559  printf("Error while opening file: %s\n",bsdPath);
1560  else
1561  {
1562  printf("opened file: %s\n",bsdPath);
1563 
1564  mSize = (int) lseek(mFile,1000000000,SEEK_SET);
1565  printf("size %d\n",mSize);
1566  lseek(mFile,0,SEEK_SET);
1567 
1568  mSize = 700 * 1024 * 1024;
1569 
1570  }
1571  }
1572  if ( mediaIterator )
1573  {
1574  IOObjectRelease( mediaIterator );
1575  }
1576 #elif LINUX
1577  OpenStream("/dev/cdrom");
1578 #endif
1579 }
1580 
1581 void DiskImage::Init()
1582 {
1583  mFile = 0;
1584  mPos = 0;
1585  mCluster = (uint)-1;
1586  mStartFrame = -1;
1587  mEndFrame = -1;
1588 #ifdef WIN32
1589  mpCache = (char*) VirtualAlloc(NULL,mClusterSize,MEM_COMMIT,PAGE_READWRITE);
1590 #else
1591  mpCache = NULL; // we allocate the cache later when we know what type of media we access
1592 #endif
1593 }
1594 
1595 DiskImage::~DiskImage()
1596 {
1597 #ifdef WIN32
1598  if (mFile != INVALID_HANDLE_VALUE)
1599  {
1600  CloseHandle(mFile);
1601  }
1602 #elif defined _CARBON_ || defined(__APPLE__) || LINUX
1603  if (mFile)
1604  {
1605  close(mFile);
1606  }
1607 #endif
1608  if (mpCache)
1609  {
1610 #ifdef WIN32
1611  VirtualFree(mpCache, 0, MEM_RELEASE);
1612 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1613  free(mpCache);
1614 #endif
1615  }
1616 }
1617 
1618 akai_stream_state_t DiskImage::GetState() const
1619 {
1620  if (!mFile) return akai_stream_closed;
1621  if (mPos > mSize) return akai_stream_end_reached;
1622  return akai_stream_ready;
1623 }
1624 
1625 int DiskImage::GetPos() const
1626 {
1627  return mPos;
1628 }
1629 
1630 int DiskImage::SetPos(int Where, akai_stream_whence_t Whence)
1631 {
1632 // printf("setpos %d\n",Where);
1633  int w = Where;
1634  switch (Whence)
1635  {
1636  case akai_stream_start:
1637  mPos = w;
1638  break;
1639  /*case eStreamRewind:
1640  w = -w;*/
1641  case akai_stream_curpos:
1642  mPos += w;
1643  break;
1644  case akai_stream_end:
1645  mPos = mSize - w;
1646  break;
1647  }
1648 // if (mPos > mSize)
1649 // mPos = mSize;
1650  if (mPos < 0)
1651  mPos = 0;
1652  return mPos;
1653 }
1654 
1655 int DiskImage::Available (uint WordSize)
1656 {
1657  return (mSize-mPos)/WordSize;
1658 }
1659 
1660 int DiskImage::Read(void* pData, uint WordCount, uint WordSize)
1661 {
1662  int readbytes = 0;
1663  int sizetoread = WordCount * WordSize;
1664 
1665  while (sizetoread > 0) {
1666  if (mSize <= mPos) return readbytes / WordSize;
1667  int requestedCluster = (mRegularFile) ? mPos / mClusterSize
1668  : mPos / mClusterSize + mStartFrame;
1669  if (mCluster != requestedCluster) { // read the requested cluster into cache
1670  mCluster = requestedCluster;
1671 #ifdef WIN32
1672  if (mCluster * mClusterSize != SetFilePointer(mFile, mCluster * mClusterSize, NULL, FILE_BEGIN)) {
1673  printf("ERROR: couldn't seek device!\n");
1674 #if 0 // FIXME: endian correction is missing correct detection
1675  if ((readbytes > 0) && (mEndian != eEndianNative)) {
1676  switch (WordSize) {
1677  case 2: bswap_16_s ((uint16*)pData, readbytes); break;
1678  case 4: bswap_32_s ((uint32*)pData, readbytes); break;
1679  case 8: bswap_64_s ((uint64*)pData, readbytes); break;
1680  }
1681  }
1682 #endif
1683  return readbytes / WordSize;
1684  }
1685  DWORD size;
1686  ReadFile(mFile, mpCache, mClusterSize, &size, NULL);
1687 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1688  if (mCluster * mClusterSize != lseek(mFile, mCluster * mClusterSize, SEEK_SET))
1689  return readbytes / WordSize;
1690 // printf("trying to read %d bytes from device!\n",mClusterSize);
1691  /*int size =*/ read(mFile, mpCache, mClusterSize);
1692 // printf("read %d bytes from device!\n",size);
1693 #endif
1694  }
1695 // printf("read %d bytes at pos %d\n",WordCount*WordSize,mPos);
1696 
1697  int currentReadSize = sizetoread;
1698  int posInCluster = mPos % mClusterSize;
1699  if (currentReadSize > mClusterSize - posInCluster) // restrict to this current cached cluster.
1700  currentReadSize = mClusterSize - posInCluster;
1701 
1702  memcpy((uint8_t*)pData + readbytes, mpCache + posInCluster, currentReadSize);
1703 
1704  mPos += currentReadSize;
1705  readbytes += currentReadSize;
1706  sizetoread -= currentReadSize;
1707 // printf("new pos %d\n",mPos);
1708  }
1709 
1710 #if 0 // FIXME: endian correction is missing correct detection
1711  if ((readbytes > 0) && (mEndian != eEndianNative))
1712  switch (WordSize)
1713  {
1714  case 2: bswap_16_s ((uint16_t*)pData, readbytes); break;
1715  case 4: bswap_32_s ((uint32_t*)pData, readbytes); break;
1716  case 8: bswap_64_s ((uint64_t*)pData, readbytes); break;
1717  }
1718 #endif
1719 
1720  return readbytes / WordSize;
1721 }
1722 
1723 void DiskImage::ReadInt8(uint8_t* pData, uint WordCount) {
1724  Read(pData, WordCount, 1);
1725 }
1726 
1727 void DiskImage::ReadInt16(uint16_t* pData, uint WordCount) {
1728  int i;
1729  for (i = 0; i < WordCount; i++) {
1730  *(pData + i) = ReadInt16();
1731  }
1732 }
1733 
1734 void DiskImage::ReadInt32(uint32_t* pData, uint WordCount) {
1735  int i;
1736  for (i = 0; i < WordCount; i++) {
1737  *(pData + i) = ReadInt32();
1738  }
1739 }
1740 
1741 int DiskImage::ReadInt8(uint8_t* pData) {
1742  return Read(pData, 1, 1);
1743 }
1744 
1745 int DiskImage::ReadInt16(uint16_t* pData) {
1746  int result = Read(pData, 1, 2);
1747 #if WORDS_BIGENDIAN
1748  swapBytes_16(pData);
1749 #endif
1750  return result;
1751 }
1752 
1753 int DiskImage::ReadInt32(uint32_t* pData) {
1754  int result = Read(pData, 1, 4);
1755 #if WORDS_BIGENDIAN
1756  swapBytes_32(pData);
1757 #endif
1758  return result;
1759 }
1760 
1761 uint8_t DiskImage::ReadInt8()
1762 {
1763  uint8_t word;
1764  Read(&word,1,1);
1765  return word;
1766 }
1767 
1768 uint16_t DiskImage::ReadInt16()
1769 {
1770  uint16_t word;
1771  Read(&word,1,2);
1772 #if WORDS_BIGENDIAN
1773  swapBytes_16(&word);
1774 #endif
1775  return word;
1776 }
1777 
1778 uint32_t DiskImage::ReadInt32()
1779 {
1780  uint32_t word;
1781  Read(&word,1,4);
1782 #if WORDS_BIGENDIAN
1783  swapBytes_32(&word);
1784 #endif
1785  return word;
1786 }
1787 
1788 void DiskImage::OpenStream(const char* path) {
1789 #ifdef WIN32
1790  mFile = CreateFile(path, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1791  BY_HANDLE_FILE_INFORMATION FileInfo;
1792  GetFileInformationByHandle(mFile,&FileInfo);
1793  mSize = FileInfo.nFileSizeLow;
1794 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1795  struct stat filestat;
1796  stat(path,&filestat);
1797  mFile = open(path, O_RDONLY | O_NONBLOCK);
1798  if (mFile <= 0) {
1799  printf("Can't open %s\n", path);
1800  mFile = 0;
1801  return;
1802  }
1803  // TODO: we should also check here if 'path' is a link or something
1804  if (S_ISREG(filestat.st_mode)) { // regular file
1805  printf("Using regular Akai image file.\n");
1806  mRegularFile = true;
1807  mSize = (int) filestat.st_size;
1808  mClusterSize = DISK_CLUSTER_SIZE;
1809  mpCache = (char*) malloc(mClusterSize);
1810  } else { // CDROM
1811 #if defined(_CARBON_) || defined(__APPLE__)
1812  printf("Can't open %s: not a regular file\n", path);
1813 #else // Linux ...
1814  mRegularFile = false;
1815  mClusterSize = CD_FRAMESIZE;
1816  mpCache = (char*) malloc(mClusterSize);
1817 
1818  struct cdrom_tochdr tochdr;
1819  struct cdrom_tocentry tocentry;
1820  int prev_addr = 0;
1821  if (ioctl(mFile, CDROMREADTOCHDR, (unsigned long)&tochdr) < 0) {
1822  printf("Trying to read TOC of %s failed\n", path);
1823  close(mFile);
1824  mFile = 0;
1825  return;
1826  }
1827  printf("Total tracks: %d\n", tochdr.cdth_trk1);
1828 
1829  /* we access the CD with logical blocks as entity */
1830  tocentry.cdte_format = CDROM_LBA;
1831 
1832  int firstDataTrack = -1;
1833  int start, end, length;
1834 
1835  /* we pick up the lowest data track by iterating backwards, starting with Lead Out */
1836  for (int t = tochdr.cdth_trk1; t >= 0; t--) {
1837  tocentry.cdte_track = (t == tochdr.cdth_trk1) ? CDROM_LEADOUT : t + 1;
1838  if (ioctl(mFile, CDROMREADTOCENTRY, (unsigned long)&tocentry) < 0){
1839  printf("Failed to read TOC entry for track %d\n", tocentry.cdte_track);
1840  close(mFile);
1841  mFile = 0;
1842  return;
1843  }
1844  if (tocentry.cdte_track == CDROM_LEADOUT) {
1845  printf("Lead Out: Start(LBA)=%d\n", tocentry.cdte_addr.lba);
1846  }
1847  else {
1848  printf("Track %d: Start(LBA)=%d End(LBA)=%d Length(Blocks)=%d ",
1849  tocentry.cdte_track, tocentry.cdte_addr.lba, prev_addr - 1, prev_addr - tocentry.cdte_addr.lba);
1850  if (tocentry.cdte_ctrl & CDROM_DATA_TRACK) {
1851  printf("Type: Data\n");
1852  firstDataTrack = tocentry.cdte_track;
1853  start = tocentry.cdte_addr.lba;
1854  end = prev_addr - 1;
1855  length = prev_addr - tocentry.cdte_addr.lba;
1856  }
1857  else printf("Type: Audio\n");
1858  }
1859  prev_addr = tocentry.cdte_addr.lba;
1860  }
1861 
1862  if (firstDataTrack == -1) {
1863  printf("Sorry, no data track found on %s\n", path);
1864  close(mFile);
1865  mFile = 0;
1866  return;
1867  }
1868 
1869  printf("Ok, I'll pick track %d\n", firstDataTrack);
1870  mStartFrame = start;
1871  mEndFrame = end;
1872  mSize = length * CD_FRAMESIZE;
1873 #endif
1874  } // CDROM
1875 #endif
1876 }
1877 
1878 bool DiskImage::WriteImage(const char* path)
1879 {
1880 #if defined(_CARBON_) || defined(__APPLE__) || LINUX
1881  const uint bufferSize = 524288; // 512 kB
1882  int fOut = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC,
1883  S_IRUSR | S_IWUSR | S_IRGRP);
1884  if (mFile <= 0) {
1885  printf("Can't open output file %s\n", path);
1886  return false;
1887  }
1888  uint8_t* pBuffer = new uint8_t[bufferSize];
1889  SetPos(0);
1890  while (Available() > 0) {
1891  int readBytes = Read(pBuffer,bufferSize,1);
1892  if (readBytes > 0) write(fOut,pBuffer,readBytes);
1893  }
1894  delete[] pBuffer;
1895  close(fOut);
1896  return true;
1897 #endif // _CARBON_ || LINUX
1898  return false;
1899 }
1900 
1901 inline void DiskImage::swapBytes_16(void* Word) {
1902  uint8_t byteCache;
1903  byteCache = *((uint8_t*) Word);
1904  *((uint8_t*) Word) = *((uint8_t*) Word + 1);
1905  *((uint8_t*) Word + 1) = byteCache;
1906 }
1907 
1908 inline void DiskImage::swapBytes_32(void* Word) {
1909  uint8_t byteCache;
1910  byteCache = *((uint8_t*) Word);
1911  *((uint8_t*) Word) = *((uint8_t*) Word + 3);
1912  *((uint8_t*) Word + 3) = byteCache;
1913  byteCache = *((uint8_t*) Word + 1);
1914  *((uint8_t*) Word + 1) = *((uint8_t*) Word + 2);
1915  *((uint8_t*) Word + 2) = byteCache;
1916 }
bool WriteImage(const char *path)
Extract Akai data track and write it into a regular file.
Definition: Akai.cpp:1878
Encapsulates one disk partition of an AKAI disk.
Definition: Akai.h:661
DiskImage(const char *path)
Open an image from a file.
Definition: Akai.cpp:1266
Toplevel AKAI image interpreter.
Definition: Akai.h:695
Accessing AKAI image either from file or a drive (i.e.
Definition: Akai.h:109
AKAI instrument definition.
Definition: Akai.h:482
Subdivision of an AKAI disk partition.
Definition: Akai.h:618
virtual int Read(void *pData, uint WordCount, uint WordSize)
Returns number of successfully read words.
Definition: Akai.cpp:1660